以前一直都是在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的代码
float4 CustomNodeWithTime(
float3 worldpos,
float3 objpos,
float3 viewdir,
float stepsize,
float steps,
float4 color,
float radius,
float time,
float3 lightdir
)
{
float3 seconcenter = { 0, 0, 0 }; float offset = 300 * sin(time);
float eoff = 0.01; seconcenter.x = objpos.x + offset;
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 outposy = { worldpos.x, worldpos.y + eoff, worldpos.z };
float3 inposy = { worldpos.x, worldpos.y - eoff, worldpos.z };
float Ydelta = distance(outposy, objpos) - distance(inposy, objpos); float3 outposz = { worldpos.x, worldpos.y, worldpos.z + eoff };
float3 inposz = { worldpos.x, worldpos.y, worldpos.z - eoff };
float Zdelta = distance(outposz, objpos) - distance(inposz, objpos); float3 normal = { Xdelta, Ydelta, Zdelta }; float3 LightColor = saturate(dot(normal, lightdir));
color.rgb = color.rgb * LightColor;
return float4(LightColor,1);
//return color;
}
worldpos += viewdir * stepsize; } return float4(0, 0, 0, 0);
}
我喜欢在VS里写代码然后再粘贴到CustomNode里