一、节点功能概述
DDX 节点是 Unity Shader Graph 中用于计算屏幕空间偏导数的核心工具,专门用于像素着色器阶段。它返回输入值相对于屏幕空间 x 坐标的偏导数,即输入值在屏幕横向方向上的变化率。这一运算对于实现高级渲染效果(如法线扰动、边缘检测、动态模糊等)至关重要,其核心优势在于:
- 实时梯度分析:通过计算相邻像素间的变化率,捕捉图像细节与边缘
- 硬件加速支持:基于 GPU 的专用指令(如 HLSL 的
ddx
函数)高效实现 - 多维度兼容性:支持标量(float)到四维向量(float4)的输入类型
二、端口详解
端口名称 | 方向 | 类型 | 描述 |
---|---|---|---|
In | 输入 | Dynamic Vector | 待求导的输入值(任意维度向量) |
Out | 输出 | Dynamic Vector | 屏幕 x 方向的偏导数(∂In/∂x) |
三、技术原理解析
3.1 数学定义
DDX 节点计算的是输入值 In 在屏幕空间 x 方向上的偏导数,数学表达式为:
在离散的像素网格中,这等价于相邻像素间的差值:
其中i为屏幕 x 方向的像素索引。
3.2 生成代码解析
节点基于 HLSL 的ddx
函数实现,示例代码如下(以 float4 输入为例):
hlsl
void Unity_DDX_float4(float4 In, out float4 Out)
{
Out = ddx(In); // 计算屏幕x方向的偏导数
}
- 计算方式:GPU 通过比较当前像素与右侧相邻像素的值来估计导数,具体实现依赖硬件优化(如使用纹理过滤单元)。
3.3 与 DDY 节点的对比
DDX 计算 x 方向偏导数,而 DDY 节点计算 y 方向偏导数。两者结合可获取完整的梯度信息(如法线重建):
四、应用场景与实战案例
4.1 法线扰动与细节增强
场景:基于高度图生成法线贴图(浮雕效果)
- 需求:通过高度图生成伪法线,增强表面细节
- 实现步骤:
hlsl
// 采样高度图 float height = SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, uv).r; // 计算x方向高度变化率 float dx = DDXNode.Out; // 输入为height // 计算y方向高度变化率(使用DDY节点) float dy = DDYNode.Out; // 输入为height // 构建法线向量(未归一化) float3 normal = float3(-dx, -dy, _NormalStrength); // 归一化法线 float3 finalNormal = normalize(normal);
4.2 边缘检测与轮廓高亮
场景:基于颜色梯度实现物体轮廓描边
- 步骤:
hlsl
// 采样基础颜色 float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); // 计算颜色在x方向的梯度 float4 colorDx = DDXNode.Out; // 输入为color // 计算颜色在y方向的梯度(使用DDY节点) float4 colorDy = DDYNode.Out; // 输入为color // 计算梯度强度(颜色变化率) float edgeIntensity = length(colorDx.rgb + colorDy.rgb); // 边缘高亮(强度超过阈值时显示轮廓色) float3 finalColor = lerp(color.rgb, _OutlineColor.rgb, step(_Threshold, edgeIntensity));
4.3 屏幕空间环境光遮蔽(SSAO)
场景:基于深度梯度模拟间接光照阴影
- 实现:
hlsl
// 从深度纹理获取当前像素深度 float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv); // 计算深度在x方向的梯度 float depthDx = DDXNode.Out; // 输入为depth // 计算深度在y方向的梯度(使用DDY节点) float depthDy = DDYNode.Out; // 输入为depth // 梯度强度表示深度变化剧烈程度(边缘区域) float edgeFactor = length(float2(depthDx, depthDy)); // 应用环境光遮蔽效果 float ao = saturate(1.0 - edgeFactor * _AOStrength);
4.4 动态模糊与速度场计算
场景:基于像素运动速度实现模糊效果
- 示例:
hlsl
// 当前帧世界位置 float3 worldPos = TransformObjectToWorld(IN.vertex.xyz); // 计算世界位置在x方向的梯度 float3 worldPosDx = DDXNode.Out; // 输入为worldPos // 估计屏幕空间x方向的速度(简化示例) float speedX = length(worldPosDx); // 应用动态模糊(沿速度方向采样多次) float4 blurredColor = SampleBlur(_MainTex, uv, float2(speedX, 0));
五、使用技巧与注意事项
5.1 仅适用于像素着色器
DDX 节点依赖像素级的相邻值比较,只能在像素着色器阶段使用。若在顶点着色器中调用,会导致编译错误或无意义结果。
5.2 输入值类型选择
- 标量输入:如高度值、深度值,输出为标量梯度(如
ddx(height)
)。 - 向量输入:如颜色、位置,输出为向量梯度(如
ddx(color.rgb)
)。
5.3 性能考量
- 计算开销:DDX/DDY 属于中等开销操作,高频调用(如每像素多次计算)时需谨慎。
- 优化建议:在性能敏感场景,可通过降低采样精度(如使用 half 精度)或预计算梯度(如法线贴图)减少实时计算量。
5.4 数值稳定性
- 边缘像素处理:屏幕边缘像素可能因缺少相邻值导致梯度计算不准确,需通过边缘检测或 clamp 操作处理。
- 浮点数精度:在大范围数值变化场景(如深度值),建议使用
ddx_fine
或ddx_coarse
等变体控制精度与性能平衡。
六、总结与拓展应用
DDX 节点作为 Shader Graph 中计算屏幕空间梯度的核心工具,其核心价值在于:
- 细节增强:通过检测颜色、高度等属性的变化率,实现边缘高亮、法线扰动等效果。
- 物理模拟:基于梯度计算实现流体模拟、动态模糊等复杂效果。
- 性能优化:相比传统基于纹理采样的方法,DDX 直接利用 GPU 硬件指令,计算效率更高。
拓展方向:
- 结合 DDX 与 DDY 实现屏幕空间曲率分析,用于程序化材质生成。
- 在后处理效果中,基于梯度实现卡通渲染的描边、深度感知的雾效等。
- 开发自定义抗锯齿算法,通过梯度检测边缘并应用特殊过滤。