因为马上就要进入下一个阶段,制作动态编辑体积纹理的模块。
但在这之前,要在这一章做最后一些整理。
- 首先,我们完成没完成的部分。
- 其次,最后整理一下图表。
- 最后,本文附上正在用的贴图
完善Shader
还记得我们之前注释掉了"阶梯纹理修复"的部分吗?
在第二章第七节中,我们已经修复了这个阶梯纹理。
CurPos += LocalCamVec * (1 - FinalStepSize);//只留了它
然而,在第三章对Shader进行大幅修改时,我们暂时将其注释掉并未继续处理。现在,我们将重新着手修复这个阶梯纹理问题。
希望你还记得,修复阶梯纹理的原理是将for循环的一步单独再以一个小步FinalStepSize
执行一次。
因此,基本上就是将for循环中的内容复制出来,在for之后再运行一次。
//创建变量,从0开始累加沿相机方向步进过程中的总密度
float accumdens = 0;
//Shadow部分
//创建变量,透射率和光线的能量
float transmittance =1;
float3 lightenergy = 0;
//基本和相机方向步进一样,但这些都是常量,不需要写进for里
Density *= StepSize;
LightVector *= ShadowStepSize;
ShadowDensity *= ShadowStepSize;
//一个对数来计算阈值,用来判断光线是否还值得计算
float shadowthresh = -log(ShadowThreshold)/ShadowDensity;
//使用 MaxSteps 作为最大步数进行循环,每次循环执行以下操作
for (int i = 0; i < MaxSteps; i++)
{
float cursample = PseudoVolumeTexture(Tex, TexSampler, saturate(CurPos), XYFrames, NumFrames).r;// 在当前步进位置进行纹理采样
//Shadow部分
if(cursample > 0.001)//如果采样位置没有密度,则跳过
{
float3 Lpos = CurPos;//Lpos将作为光线步进的起始位置
float shadowdist = 0;//和之前的accumdens一样,积累阴影
//自阴影
for(int s = 0; s < ShadowSteps; s++)
{
Lpos += LightVector;//移动步进位置
float Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样
//判断是否在框内,不是则直接break退出for
float3 shadowboxtest = floor( 0.5+ (abs(0.5-Lpos)));
//float exitshadowbox = shadowboxtest.x + shadowboxtest.y + shadowboxtest.z;
float exitshadowbox = dot(shadowboxtest,1);//简短的通道相加
if(shadowdist > shadowthresh || exitshadowbox >= 1) break;
shadowdist += Lsample;//累计
}
//接收阴影
float3 dfpos = 2 * (CurPos -0.5) * LocalObjectBoundsMax;//-0.5 * 2,得到一个居中的Bound
dfpos = LWCToFloat(TransformLocalPositionToWorld(Parameters,dfpos)) - CameraPosWS;//将dfpos转换为世界空间,需要LWC精度所以在代码里转换,减去相机位置
float dftracedist = 1; //创建四个变量
float dfshadow = 1;//这是我们最终要的
float curdist = 0;
float DistanceAlongTrace = 0;
for (int d = 0; d < DFSSteps; d++)//又一次的光线步进
{
DistanceAlongTrace += curdist;//增加距离
curdist = GetDistanceToNearestSurfaceGlobal(dfpos);//采样全局距离场,他和蓝图里`DistanceToNearestSurface`是相同函数
float SphereSize = DistanceAlongTrace * LightTangent;//采样距离场软阴影的球形距离
dfshadow = min( saturate(curdist/SphereSize),dfshadow);//用小于它的结果来更新变量
dfpos.xyz += LightVectorWS * dftracedist * curdist;//继续移动位置
dftracedist *= 1.0001;//增加一个很小的因子
}
//更新样本和光能,算法是BeersLaw函数
cursample = 1 -exp(-cursample * Density);
lightenergy += exp(-shadowdist * ShadowDensity) * cursample * transmittance * LightColor * dfshadow;//在结果上乘dfshadow
transmittance *= 1-cursample;
//环境光照部分
shadowdist = 0;//重置一下阴影距离,继续利用它计算光照
Lpos = CurPos + float3(0,0,0.025);//新位置
float Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样
shadowdist += Lsample;
Lpos = CurPos + float3(0,0,0.05);
Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样
shadowdist += Lsample;
Lpos = CurPos + float3(0,0,0.15);
Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样
shadowdist += Lsample;
lightenergy += exp(-shadowdist * AmbientDensity) *cursample * SkyColor * transmittance;//累计到光
}
CurPos += -LocalCamVec;
}
CurPos += LocalCamVec * (1 - FinalStepSize);
float cursample = PseudoVolumeTexture(Tex, TexSampler, saturate(CurPos), XYFrames, NumFrames).r;
//从上面复制过来,使用 FinalStepSize 结果再 Step 一次,进行阶梯修复
if(cursample > 0.001)
{
float3 Lpos = CurPos;
float shadowdist = 0;
for(int s = 0; s < ShadowSteps; s++)
{
Lpos += LightVector;
float Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;
float3 shadowboxtest = floor( 0.5+ (abs(0.5-Lpos)));
float exitshadowbox = dot(shadowboxtest,1);
if(shadowdist > shadowthresh || exitshadowbox >= 1) break;
shadowdist += Lsample;
}
float3 dfpos = 2 * (CurPos -0.5) * LocalObjectBoundsMax;
dfpos = LWCToFloat(TransformLocalPositionToWorld(Parameters,dfpos)) - CameraPosWS;
float dftracedist = 1;
float dfshadow = 1;
float curdist = 0;
float DistanceAlongTrace = 0;
for (int d = 0; d < DFSSteps; d++)
{
DistanceAlongTrace += curdist;
curdist = GetDistanceToNearestSurfaceGlobal(dfpos);
float SphereSize = DistanceAlongTrace * LightTangent;
dfshadow = min( saturate(curdist/SphereSize),dfshadow);
dfpos.xyz += LightVectorWS * dftracedist * curdist;
dftracedist *= 1.0001;
}
cursample = 1 -exp(-cursample * Density);
lightenergy += exp(-shadowdist * ShadowDensity) * cursample * transmittance * LightColor * dfshadow;
transmittance *= 1-cursample;
shadowdist = 0;
Lpos = CurPos + float3(0,0,0.025);
float Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;
shadowdist += Lsample;
Lpos = CurPos + float3(0,0,0.05);
Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;
shadowdist += Lsample;
Lpos = CurPos + float3(0,0,0.15);
Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;
shadowdist += Lsample;
lightenergy += exp(-shadowdist * AmbientDensity) *cursample * SkyColor * transmittance;
}
return float4(lightenergy, transmittance);
整理图表
老样子,我们做整理,消除意大利面可以让我们更直观的感受shader中的各种关系
-
将这一部分
RayMarching
的参数折叠为RayMarchingParameter
-
打包环境和常量参数
Constant
-
整理自阴影距离场的变量
SelfShadow
-
打包投影的参数
ShadowRayParameter
-
最后整理一下
ShadowRayMarching
输入顺序,按功能排序
当前Shader
抄抄党注目
模型
长宽高100cm,轴居中,双面双材质ID的Cube模型
本文附下载
预览贴图
长宽高100cm,轴居中,双面双材质ID的Cube模型。
本文附下载
材质球
M_VolRayMarching
MI_VolRayMarching
MI_VolRayMarching_Shadow
M_VolRayMarching
细节
图表
MI_VolRayMarching
父材质为M_VolRayMarching
MI_VolRayMarching_Shadow
父材质为MI_VolRayMarching
- 细节: