虚幻4中实现简单的raymarch
来源:互联网 发布:nginx 跳转到指定ip 编辑:程序博客网 时间:2024/06/06 04:54
以前一直都是在Directx或者UnityShaderLab里做raymarch,最近在研究虚幻4的Shader所以在虚幻4里简单实现了一下这个。
我的step数量调得很低。
刚开始其实不好下手,虚幻的渲染架构过于复杂,高度封装,我们无法直接像unityshaderlab一样对shader进行直接编写,如果想直接自己上传shader而绕过虚幻的材质系统,那么成本将是高昂的。首先先简单捋一下虚幻的shader生成过程。第一步我们在材质系统里完成各种节点的编写,这些节点是c++写的,可以在源码的MaterialExpression里找到全部实现。这些节点带有一个Fstring的字符串,里面保存的是HLSL代码。在材质编译的时候,HLSLTranslator会把这些字符串插入到usf提供的shader模板中,最后生成我们需要的shader。生成后的shader可以在材质编辑器的HLSLCode里看到。如果我们想在CustomNode里调用自己的函数,可以在CommonUSF文件中添加。但是可能会导致引擎崩溃,直到4.17版本,虚幻将usf文件暴露出来,在4.17版本我们就能给自己的项目添加usf从而CustomNode就能调用函数了。除此之外,虚幻的材质如何模拟多pass行为也是一个难题,目前4.17的方式是使用多个Render Target来模拟,但是效率低下。
下面是我ray mach的代码
(
float3 worldpos,
float3 objpos,
float3 viewdir,
float stepsize,
float steps,
float4 color,
float radius,
float time,
float3 lightdir
)
{
float3 seconcenter = { 0, 0, 0 };
float eoff = 0.01;
seconcenter.z = objpos.z + offset;
seconcenter.y = objpos.y;
for (int i = 0; i <= steps; i++)
{
if ((distance(worldpos, objpos) <= radius) || (distance(worldpos, seconcenter) <= radius - 100))
{
float3 outposx = { worldpos.x + eoff, worldpos.y, worldpos.z };
float3 inposx = { worldpos.x - eoff, worldpos.y, worldpos.z };
float Xdelta = distance(outposx, objpos) - distance(inposx, objpos);
float3 inposy = { worldpos.x, worldpos.y - eoff, worldpos.z };
float Ydelta = distance(outposy, objpos) - distance(inposy, objpos);
float3 inposz = { worldpos.x, worldpos.y, worldpos.z - eoff };
float Zdelta = distance(outposz, objpos) - distance(inposz, objpos);
color.rgb = color.rgb * LightColor;
return float4(LightColor,1);
//return color;
}
worldpos += viewdir * stepsize;
}
我喜欢在VS里写代码然后再粘贴到CustomNode里
注:直接把上面的代码复制粘贴到代码里会报错,不清楚为什么,如果在vs里重新敲一次却不会。估计网页里有些符号和代码里的不一样,虽然他们看起来一样。
顺便总结一下常用Raymarching模型(这些公式来自国外一个朋友)
Sphere - signed - exact
float sdSphere( vec3 p, float s ){ return length(p)-s;}
Box - unsigned - exact
float udBox( vec3 p, vec3 b ){ return length(max(abs(p)-b,0.0));}
Round Box - unsigned - exact
float udRoundBox( vec3 p, vec3 b, float r ){ return length(max(abs(p)-b,0.0))-r;}
Box - signed - exact
float sdBox( vec3 p, vec3 b ){ vec3 d = abs(p) - b; return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));}
Torus - signed - exact
float sdTorus( vec3 p, vec2 t ){ vec2 q = vec2(length(p.xz)-t.x,p.y); return length(q)-t.y;}
Cylinder - signed - exact
float sdCylinder( vec3 p, vec3 c ){ return length(p.xz-c.xy)-c.z;}
Cone - signed - exact
float sdCone( vec3 p, vec2 c ){ // c must be normalized float q = length(p.xy); return dot(c,vec2(q,p.z));}
Plane - signed - exact
float sdPlane( vec3 p, vec4 n ){ // n must be normalized return dot(p,n.xyz) + n.w;}
Hexagonal Prism - signed - exact
float sdHexPrism( vec3 p, vec2 h ){ vec3 q = abs(p); return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x);}
Triangular Prism - signed - exact
float sdTriPrism( vec3 p, vec2 h ){ vec3 q = abs(p); return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5);}
Capsule / Line - signed - exact
float sdCapsule( vec3 p, vec3 a, vec3 b, float r ){ vec3 pa = p - a, ba = b - a; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); return length( pa - ba*h ) - r;}
Capped cylinder - signed - exact
float sdCappedCylinder( vec3 p, vec2 h ){ vec2 d = abs(vec2(length(p.xz),p.y)) - h; return min(max(d.x,d.y),0.0) + length(max(d,0.0));}
Capped Cone - signed - bound
float sdCappedCone( in vec3 p, in vec3 c ){ vec2 q = vec2( length(p.xz), p.y ); vec2 v = vec2( c.z*c.y/c.x, -c.z ); vec2 w = v - q; vec2 vv = vec2( dot(v,v), v.x*v.x ); vec2 qv = vec2( dot(v,w), v.x*w.x ); vec2 d = max(qv,0.0)*qv/vv; return sqrt( dot(w,w) - max(d.x,d.y) ) * sign(max(q.y*v.x-q.x*v.y,w.y));}
Ellipsoid - signed - bound
float sdEllipsoid( in vec3 p, in vec3 r ){ return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z);}
Triangle - unsigned - exact
float dot2( in vec3 v ) { return dot(v,v); }float udTriangle( vec3 p, vec3 a, vec3 b, vec3 c ){ vec3 ba = b - a; vec3 pa = p - a; vec3 cb = c - b; vec3 pb = p - b; vec3 ac = a - c; vec3 pc = p - c; vec3 nor = cross( ba, ac ); return sqrt( (sign(dot(cross(ba,nor),pa)) + sign(dot(cross(cb,nor),pb)) + sign(dot(cross(ac,nor),pc))<2.0) ? min( min( dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa), dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ), dot2(ac*clamp(dot(ac,pc)/dot2(ac),0.0,1.0)-pc) ) : dot(nor,pa)*dot(nor,pa)/dot2(nor) );}
Quad - unsigned - exact
float dot2( in vec3 v ) { return dot(v,v); }float udQuad( vec3 p, vec3 a, vec3 b, vec3 c, vec3 d ){ vec3 ba = b - a; vec3 pa = p - a; vec3 cb = c - b; vec3 pb = p - b; vec3 dc = d - c; vec3 pc = p - c; vec3 ad = a - d; vec3 pd = p - d; vec3 nor = cross( ba, ad ); return sqrt( (sign(dot(cross(ba,nor),pa)) + sign(dot(cross(cb,nor),pb)) + sign(dot(cross(dc,nor),pc)) + sign(dot(cross(ad,nor),pd))<3.0) ? min( min( min( dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa), dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ), dot2(dc*clamp(dot(dc,pc)/dot2(dc),0.0,1.0)-pc) ), dot2(ad*clamp(dot(ad,pd)/dot2(ad),0.0,1.0)-pd) ) : dot(nor,pa)*dot(nor,pa)/dot2(nor) );}
Torus82 - signed
float sdTorus82( vec3 p, vec2 t ){ vec2 q = vec2(length2(p.xz)-t.x,p.y); return length8(q)-t.y;}
Torus88 - signed
float sdTorus88( vec3 p, vec2 t ){ vec2 q = vec2(length8(p.xz)-t.x,p.y); return length8(q)-t.y;}
距离场运算
Union
float opU( float d1, float d2 ){ return min(d1,d2);}
Substraction
float opS( float d1, float d2 ){ return max(-d1,d2);}
Intersection
float opI( float d1, float d2 ){ return max(d1,d2);}
Repetition
float opRep( vec3 p, vec3 c ){ vec3 q = mod(p,c)-0.5*c; return primitve( q );}
Rotation/Translation
vec3 opTx( vec3 p, mat4 m ){ vec3 q = invert(m)*p; return primitive(q);}
Scale
float opScale( vec3 p, float s ){ return primitive(p/s)*s;}
distance deformations
You must be carefull when using distance transformation functions, as the field created might not be a real distance function anymore. You will probably need to decrease your step size, if you are using a raymarcher to sample this. The displacement example below is using sin(20*p.x)*sin(20*p.y)*sin(20*p.z) as displacement pattern, but you can of course use anything you might imagine. As for smin() function in opBlend(), please read thesmooth minimum article in this same site.
Displacement
float opDisplace( vec3 p ){ float d1 = primitive(p); float d2 = displacement(p); return d1+d2;}
Blend
float opBlend( vec3 p ){ float d1 = primitiveA(p); float d2 = primitiveB(p); return smin( d1, d2 );}
domain deformations
Domain deformation functions do not preserve distances neither. You must decrease your marching step to properly sample these functions (proportionally to the maximun derivative of the domain distortion function). Of course, any distortion function can be used, from twists, bends, to random noise driven deformations.
Twist
float opTwist( vec3 p ){ float c = cos(20.0*p.y); float s = sin(20.0*p.y); mat2 m = mat2(c,-s,s,c); vec3 q = vec3(m*p.xz,p.y); return primitive(q);}
Cheap Bend
float opCheapBend( vec3 p ){ float c = cos(20.0*p.y); float s = sin(20.0*p.y); mat2 m = mat2(c,-s,s,c); vec3 q = vec3(m*p.xy,p.z); return primitive(q);}
- 虚幻4中实现简单的raymarch
- 虚幻4制作简单手雷的注意事项
- 虚幻4 C++ 九宫格的实现
- 虚幻4游戏菜单功能的实现
- Shader特效——“Simple 3D Raymarch”的实现 【GLSL】
- 虚幻4的Style
- 虚幻4中变量被优化掉的解决方案
- 在虚幻4中显示Kinect2.0摄像头的画面
- 虚幻4中,android第三方类库的接入
- 虚幻4中SceneDepth , PixelDepth ,Customdepth,CustomDepth StencilValue的区别
- 虚幻4的智能指针
- 虚幻4 材质的编译。
- 虚幻4 shader的使用
- 虚幻引擎与Unity引擎中逼真冰块效果的实现
- 使用C++实现虚幻4通电游戏
- 开始使用虚幻4并创建一个简单的c++类
- 在虚幻4中使用kinect2.0
- 虚幻4中模型勾边
- pip安装whl文件及tensorflow安装
- 读取外部XX.Proper文件内的信息
- 嵌入式设备系统的烧写过程
- MMO手游地图同步方案的分析与总结
- Window-UI
- 虚幻4中实现简单的raymarch
- 去掉字符串中空格的四种方法
- 徽章系列3: Travis CI 的使用
- SQL左右连接中的on and和on where的区别
- 数学分析
- arm开发板烧写
- 面向对象_基本概念(2)
- 徽章系列8:生成个性徽章
- linux系统网络配置