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

  • 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数据
/* by 01130.hk - online tools website : 01130.hk/zh/createmeta.html */
// hlsl
Shader "Custom/NDCDemo"
{
    SubShader
    {
        Pass
        {
            HLSLPROGRAM
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes { float4 vertex : POSITION; };
            struct Varyings { float4 pos : SV_POSITION; float3 ndc : TEXCOORD0; };

            Varyings vert(Attributes v)
            {
                Varyings o;
                o.pos = TransformObjectToHClip(v.vertex.xyz);
                // 手动计算NDC坐标
                o.ndc = o.pos.xyz / o.pos.w; 
                return o;
            }
            ENDHLSL
        }
    }
}

URP中的NDC

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

  1. Direct3D风格平台‌(如Windows、Xbox等):
    • NDC范围是 ‌[-1, 1]³‌(x,y,z三个维度)
    • 深度值(z)映射到[0,1(通过投影矩阵转换)
  2. OpenGL风格平台‌(如MacOS、Linux等):
    • NDC范围是 ‌[-1, 1]³
    • 深度值(z)保持[-1,1]

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

坐标转换示例过程

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

  1. 通过视图矩阵转换到视图空间(相机空间)
  2. 通过URP投影矩阵转换到裁剪空间(clip space)
  3. 透视除法得到NDC坐标(w分量除法)

具体数值示例(假设使用D3D风格):

世界坐标 (2, 1, 5)
↓ 视图矩阵转换
视图坐标 (1.5, 0.8, 4.2)
↓ 投影矩阵转换
裁剪坐标 (3.2, 1.6, 8.4, 4.2)
↓ 透视除法 (x/w, y/w, z/w)
NDC坐标 (0.76, 0.38, 2.0) → 超出[-1,1]会被裁剪

深度值特殊处理

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

  • 原始NDC的z ∈ [-1,1](OpenGL)或 [0,1](D3D)
  • 最终存储到深度纹理时统一映射到[0,1]范围

可以通过Shader验证:

/* by 01130.hk - online tools website : 01130.hk/zh/createmeta.html */
hlsl
// 在Fragment Shader中:
float ndcZ = clipPos.z / clipPos.w; // 透视除法后的z值
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矩阵进行完整坐标空间转换链验证。


Shader "Custom/NDCGeometryShader"
{
    Properties { _MainTex ("Texture", 2D) = "white" {} }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag
            #include "Packages/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<g2f> stream) {
                // 手动计算NDC坐标
                float4 clipPos = input[0].pos;
                float3 ndc = clipPos.xyz / clipPos.w;

                // 屏幕空间扩展(生成四边形粒子)
                float size = 0.1;
                g2f o;
                for(int i=0; i<4; i++) {
                    o.pos = clipPos;
                    o.pos.xy += float2((i%2)*2-1, (i/2)*2-1) * size * clipPos.w;
                    o.uv = input[0].uv;
                    o.ndc = ndc;
                    stream.Append(o);
                }
                stream.RestartStrip();
            }

            half4 frag(g2f i) : SV_Target {
                // 使用NDC坐标采样屏幕纹理
                float2 screenUV = i.ndc.xy * 0.5 + 0.5;
                return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, screenUV);
            }
            ENDHLSL
        }
    }
}

【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)内容概要:本文档围绕六自由度机械臂的ANN人工神经网络设计展开,详细介绍了正向与逆向运动学求解、正向动力学控制以及基于拉格朗日-欧拉法推导逆向动力学方程的理论与Matlab代码实现过程。文档还涵盖了PINN物理信息神经网络在微分方程求解、主动噪声控制、天线分析、电动汽车调度、储能优化等多个工程与科研领域的应用案,并提供了丰富的Matlab/Simulink仿真资源和技术支持方向,体现了其在多学科交叉仿真与优化中的综合性价值。; 适合人群:具备一定Matlab编程基础,从事机器人控制、自动化、智能制造、电力系统或相关工程领域研究的科研人员、研究生及工程师。; 使用场景及目标:①掌握六自由度机械臂的运动学与动力学建模方法;②学习人工神经网络在复杂非线性系统控制中的应用;③借助Matlab实现动力学方程推导与仿真验证;④拓展至路径规划、优化调度、信号处理等相关课题的研究与复现。; 阅读建议:建议按目录顺序系统学习,重点关注机械臂建模与神经网络控制部分的代码实现,结合提供的网盘资源进行实践操作,并参考文中列举的优化算法与仿真方法拓展自身研究思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值