← ALL ARTICLES
VFX与模拟2 MIN READ

溶解材质

全库目录221
本文目录5

阈值推进时,溶解边缘应沿噪声场移动,而阴影与本体不能各用一份阈值。溶解材质把覆盖和边缘颜色分开求值,再接入统一的深度、阴影与Motion路径。

标量场与覆盖

Dissolve 用标量场 m(x)m(\mathbf{x}) 与阈值 tt 比较:

visible=m(x)≥t\text{visible}=m(\mathbf{x})\ge t

标量场可以来自噪声纹理、顶点色、世界位置、对象空间梯度或 SDF。

边缘带可写成:

e=smoothstep⁡(t,t+w,m)−smoothstep⁡(t+w,t+2w,m)e=\operatorname{smoothstep}(t,t+w,m)-\operatorname{smoothstep}(t+w,t+2w,m)

ww 控制边宽。将 ee 用于自发光/颜色,可产生燃烧边缘。

对象空间遮罩随物体稳定;世界空间可让多个物体共享扫描平面;UV 噪声会跟随贴图,但可能在 UV 缝断裂。选择应符合效果语义。

使用 clip/discard 能保留不透明排序和深度写入,但边缘会锯齿,也可能削弱 Early-Z。Alpha Blend 更柔和,却带来排序和重复绘制。阴影、深度、运动向量 Pass 应使用一致阈值,否则影子和轮廓不同步。

覆盖率与发光边缘

输入噪声和阈值都位于 [0,1][0,1]。fwidth 根据屏幕像素覆盖调整边缘宽度,输出包含裁剪结果与发光边缘:

hlsl
float noise = DissolveNoise.Sample(LinearRepeat, uv).r;
float signedDistance = noise - threshold;
float pixelWidth = max(fwidth(signedDistance), 1e-4);
float coverage = smoothstep(-pixelWidth, pixelWidth, signedDistance);
float edge = saturate(1.0 - abs(signedDistance) / max(edgeWidth, pixelWidth));
clip(coverage - alphaCutoff);
float3 color = baseColor + edge * edgeColor * edgeIntensity;

例如噪声0.55、阈值0.5、像素宽度0.1,覆盖为smoothstep(-0.1,0.1,0.05)=0.84375。裁剪阈值0.5时该像素留下,但输出覆盖并没有自动变成84.375%;若没有把coverage传入具体覆盖方案,像素仍整片写入。阈值升到0.7时同一位置位于裁剪侧,由此可以逐帧预测边界移动。

这里的 edgeWidth 属于噪声值域,不是世界距离。噪声对比度或 UV 缩放改变时,视觉边宽也会改变。这里计算了连续覆盖率,但 clip 又把它变成二值裁剪,单靠这段代码不能得到平滑像素覆盖。要利用 coverage,还需接入 Alpha-to-Coverage、透明混合或时域抖动等覆盖方案。验证时分别观察遮罩、裁剪和最终覆盖,判断变化发生在哪一层。

阈值端点需要单独约定。由于 m >= t 包含等号,纹理中值恰为 1 的区域在 t=1 时仍保留;若要求动画末帧完全消失,可把阈值推进到数据最大值之外,或由完成状态直接关闭绘制。阴影、深度和颜色使用同一完成状态,否则本体消失以后仍可能留下阴影。

验证方法

显示噪声、signedDistance、coverage和最终裁剪。阈值归零与到一时检查预期覆盖,UV缩放改变边宽时区分值域和像素单位。fwidth得到连续覆盖率后还要通过Alpha-to-Coverage或时间抖动使用,clip仍是二值。

实现状态:正文片段用于解释本页数据流与边界条件;完整宿主的编译、运行及性能测量尚未执行。可运行的独立模型以本页列出的具体测试为准。

相关主题

参考资料

  • Microsoft:clip:负值丢弃的端点语义;正文阈值和发光带按该语义构造。