Unity Shader中ddx ddy求法线方法

132 阅读3分钟

前言

在Unity Shader中,可以通过ddxddy函数间接求出经过顶点变换后的法线,但这是一种特定场景下的替代方案,并非标准做法。以下是详细解释:

对惹,这里有一个游戏开发交流小组 ,希望大家可以点击进来一起交流一下开发经验呀!

核心原理

  1. ddx ddy 的作用
    这两个函数在片段着色器中计算屏幕空间中的偏导数(梯度):
  • ddx(var):当前像素与右侧像素的差值。

  • ddy(var):当前像素与下方像素的差值。
    它们基于屏幕像素网格,适用于任何插值后的变量(如位置、UV等)。

  • 法线重建的原理
    若在片段着色器中已有视图空间(View Space)或世界空间(World Space)的位置,可通过以下步骤重建法线:

// 假设 i.posWS 是世界空间位置
float3 dpdx = ddx(i.posWS); // 屏幕X方向的位置梯度
float3 dpdy = ddy(i.posWS); // 屏幕Y方向的位置梯度
float3 faceNormal = normalize(cross(dpdy, dpdx)); // 叉积得到几何法线

关键细节

  1. 得到的是面法线(几何法线)
  • 此法线基于三角形面的实际朝向,未经过顶点法线的平滑插值。

  • 效果较“硬”,适合需要棱角感的表面(如岩石、低模风格)。

  • 坐标系需一致

  • 位置变量必须位于同一空间(如统一为世界空间或视图空间)。

  • 叉积顺序 cross(dpdy, dpdx) 需匹配渲染管线的坐标系(右手/左手)。

  • 性能考量

  • ddx/ddy 是硬件加速操作,效率较高。

  • 但需在片段着色器中额外计算,若原始法线已通过插值获得,直接使用插值法线更高效。

与标准方法的对比

方法优点缺点
ddx/ddy重建法线无需传递法线属性,节省顶点数据仅得面法线,无平滑插值效果
插值顶点法线支持平滑着色需传递法线并插值,占用带宽

示例代码(Unity ShaderLab)

Shader "Custom/DDXNormal" {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float4 pos : SV_POSITION;
                float3 posWS : TEXCOORD0; // 世界空间位置
            };

            v2f vert (appdata_base v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.posWS = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                // 用ddx/ddy重建法线
                float3 dpdx = ddx(i.posWS);
                float3 dpdy = ddy(i.posWS);
                float3 normal = normalize(cross(dpdy, dpdx));

                // 可视化法线(转换到[0,1]范围)
                return fixed4(normal * 0.5 + 0.5, 1);
            }
            ENDCG
        }
    }
}

适用场景

  1. 无预算法线时
  • 如深度纹理后处理、屏幕空间效果(SSR, SSAO)。

  • 需要几何真实法线时

  • 如CAD可视化、硬表面渲染。

  • 节省顶点数据

  • 避免向片段着色器传递法线属性。

注意事项

  • 背面问题:重建的法线方向可能与需求相反,可用 face = sign(dot(faceNormal, viewDir)) 校正。
  • 非线性变换:若模型有非均匀缩放,需在视图/世界空间计算,避免物体空间畸变。
  • 边缘不连续:在UV或位置不连续处(如硬边),ddx/ddy 可能产生 artifacts。

结论

可以通过 ddx/ddy 求出变换后的法线,但结果本质是屏幕空间重建的几何面法线。若需要平滑插值的顶点法线,仍应通过顶点着色器传递并插值。根据需求权衡选择方案。

更多教学视

Unity3D​www.bycwedu.com/promotion_channels/2146264125