找回密码
 立即注册
首页 业界区 业界 【渲染流水线】[几何阶段]-[归一化NDC]以UnityURP为例 ...

【渲染流水线】[几何阶段]-[归一化NDC]以UnityURP为例

绂染 2025-9-28 16:46:48

  • NDC空间‌:透视除法的结果,顶点坐标已归一化,可直接用于视口映射和裁剪‌
【从UnityURP开始探索游戏渲染】专栏-直达


  • 在渲染管线中,‌归一化严格等同于透视除法‌,是齐次坐标到NDC空间转换的核心步骤‌。Unity中这步,自动执行。
  • 数据归一化主要通过‌NDC空间(归一化设备坐标)转换‌实现,其核心原理是将裁剪空间坐标统一映射到标准范围([-1,1]的立方体内(OpenGL标准)或[0,1](DirectX标准))
  • 可以看作是一个矩形内的坐标体系。经过转化后的坐标体系是 限制在一个立方体内的坐标体系。无论x y z轴在坐标体系内的范围都是(-1, 1)。归一化后,z轴向屏幕内。
  • 归一化范围在OpenGL中范围为[-1, 1],DirectX中为[0, 1]。映射到屏幕时(0, 0)点:GpenGL是左下角,DirectX是左上角。
归一化原理

透视除法(Perspective Division)

将齐次裁剪空间坐标的(x,y,z)分量除以w分量,得到NDC坐标
此操作将坐标归一化至[-1,1]范围(OpenGL/Unity)或[0,1]范围(Direct3D)‌。
NDCExample.shader


  • 1.URP标准坐标转换流程
  • 2.手动NDC坐标计算
  • 3.通过v2f结构传递NDC数据
  1. // hlsl
  2. Shader "Custom/NDCDemo"
  3. {
  4.     SubShader
  5.     {
  6.         Pass
  7.         {
  8.             HLSLPROGRAM
  9.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
  10.             struct Attributes { float4 vertex : POSITION; };
  11.             struct Varyings { float4 pos : SV_POSITION; float3 ndc : TEXCOORD0; };
  12.             Varyings vert(Attributes v)
  13.             {
  14.                 Varyings o;
  15.                 o.pos = TransformObjectToHClip(v.vertex.xyz);
  16.                 // 手动计算NDC坐标
  17.                 o.ndc = o.pos.xyz / o.pos.w;
  18.                 return o;
  19.             }
  20.             ENDHLSL
  21.         }
  22.     }
  23. }
复制代码
URP中的NDC

Unity URP(Universal Render Pipeline)中,归一化的设备坐标(NDC)映射范围取决于具体的API平台:

  • Direct3D风格平台‌(如Windows、Xbox等):

    • NDC范围是 ‌[-1, 1]³‌(x,y,z三个维度)
    • 深度值(z)映射到[0,1(通过投影矩阵转换)

  • OpenGL风格平台‌(如MacOS、Linux等):

    • NDC范围是 ‌[-1, 1]³
    • 深度值(z)保持[-1,1]

URP默认使用‌[-1,1]³‌的NDC范围(与Built-in管线一致),但最终会适配目标平台的约定。
坐标转换示例过程

假设有一个世界空间点(2, 1, 5):

  • 通过视图矩阵转换到视图空间(相机空间)
  • 通过URP投影矩阵转换到裁剪空间(clip space)
  • 透视除法得到NDC坐标(w分量除法)
具体数值示例(假设使用D3D风格):
  1. 世界坐标 (2, 1, 5)
  2. ↓ 视图矩阵转换
  3. 视图坐标 (1.5, 0.8, 4.2)
  4. ↓ 投影矩阵转换
  5. 裁剪坐标 (3.2, 1.6, 8.4, 4.2)
  6. ↓ 透视除法 (x/w, y/w, z/w)
  7. NDC坐标 (0.76, 0.38, 2.0) → 超出[-1,1]会被裁剪
复制代码
深度值特殊处理

在URP中,深度缓冲区的值会被重新映射:

  • 原始NDC的z ∈ [-1,1](OpenGL)或 [0,1](D3D)
  • 最终存储到深度纹理时统一映射到[0,1]范围
可以通过Shader验证:
  1. hlsl
  2. // 在Fragment Shader中:
  3. float ndcZ = clipPos.z / clipPos.w; // 透视除法后的z值
  4. float depth = ndcZ * 0.5 + 0.5;    // D3D平台下实际存储值
复制代码
URP通过_UNITY_UV_STARTS_AT_TOP等宏处理不同平台的坐标差异,保证跨平台一致性。
NDC转换在实际中的应用

虽然默认NDC计算是固定加速计算的过程,但是有时需要手动计算实现一些定制效果。
在Unity URP中,几何着色器(Geometry Shader)手动计算NDC并实现屏幕映射的典型应用场景包括:
1. 视锥裁剪

  • 将世界坐标转换为NDC后判断是否在[-1,1]范围内
2. 屏幕空间特效

  • ‌ 通过NDC坐标计算UV用于采样屏幕纹理
3. 几何体动态生成

  • ‌  根据NDC坐标控制顶点生成范围
计算NDC并实现屏幕空间粒子生成示例ScreenSpaceParticle.shader


  • 在几何着色器中通过clipPos.xyz / clipPos.w完成透视除法得到NDC坐标
  • 使用NDC坐标时需注意:

    • D3D平台下y轴需要取反(screenUV.y = 1 - screenUV.y)
    • 深度值在D3D平台需映射到[0,1]范围

  • 示例实现了屏幕空间粒子生成效果,可通过NDC坐标控制生成范围
实际应用时可结合_UNITY_MATRIX_VP矩阵进行完整坐标空间转换链验证。
[code]Shader "Custom/NDCGeometryShader"{    Properties { _MainTex ("Texture", 2D) = "white" {} }    SubShader    {        Tags { "RenderType"="Opaque" }        Pass        {            HLSLPROGRAM            #pragma vertex vert            #pragma geometry geom            #pragma fragment frag            #include "ackages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            struct v2g {                float4 pos : SV_POSITION;                float2 uv : TEXCOORD0;            };            struct g2f {                float4 pos : SV_POSITION;                float2 uv : TEXCOORD0;                float3 ndc : TEXCOORD1;            };            v2g vert(appdata_base v) {                v2g o;                o.pos = TransformObjectToHClip(v.vertex);                o.uv = v.texcoord;                return o;            }            [maxvertexcount(4)]            void geom(point v2g input[1], inout TriangleStream stream) {                // 手动计算NDC坐标                float4 clipPos = input[0].pos;                float3 ndc = clipPos.xyz / clipPos.w;                // 屏幕空间扩展(生成四边形粒子)                float size = 0.1;                g2f o;                for(int i=0; i

相关推荐

前天 13:32

举报

谢谢分享,试用一下
您需要登录后才可以回帖 登录 | 立即注册