【渲染流水线】[几何阶段]-[归一化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数据
// 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平台:
- 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风格):
世界坐标 (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验证:
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]范围
- D3D平台下y轴需要取反(
- 示例实现了屏幕空间粒子生成效果,可通过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开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,)
【渲染流水线】[几何阶段]-[归一化NDC]以UnityURP为例的更多相关文章
- 《UnityShader入门精要》学习笔记之渲染流水线
第一种分类方式: 图形管道(如下7步): 顶点数据 : 由3D模型传递的三角形网格 顶点着色 : 编写CG程序对各个顶点进行着色 生成几何图元 : 连接特定的顶点生成几何图元,例如连接三个顶点生成一个 ...
- Unity Shader入门精要学习笔记 - 第2章 渲染流水线
来源作者:candycat http://blog.csdn.net/candycat1992/article/ 2.1 综述 渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第五章:渲染流水线
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第五章:渲染流水线 学习目标 了解几个用以表达真实场景的标志和2D图像 ...
- GPU渲染流水线的简单概括
GPU流水线 主要分为两个阶段:几何阶段和光栅化阶段 几何阶段 顶点着色器 --> 曲面细分着色器(可选)----->几何着色器(可选)----->裁剪-->屏幕 ...
- Shader 入门笔记(二) CPU和GPU之间的通信,渲染流水线
渲染流水线 1)应用阶段(CPU处理) 首先,准备好场景数据(摄像机位置,视锥体,模型和光源等) 接着,做粗粒度剔除工作. 最后,设置好每个模型的渲染状态(使用的材质,纹理,shader等) 这一阶段 ...
- Unity Shader 之 渲染流水线
Unity Shader 之渲染流水线 什么是渲染流水线 一个渲染流程分成3个步骤: 应用阶段(Application stage) 几何阶段(Geometry stage) 光栅化阶段(Raster ...
- Unity 渲染流水线 :CPU与GPU合作创造的艺术wfd
前言 对于Unity渲染流程的理解可以帮助我们更好对Unity场景进行性能消耗的分析,进而更好的提升场景渲染的效率,最后提升游戏整体的性能表现 Unity的游戏画面的最终的呈现是由CPU与GPU相互配 ...
- 移动端 像素渲染流水线与GPU Hack
什么是 像素渲染流水线 web页面你所写的页面代码是如何被转换成屏幕上显示的像素的.这个转换过程可以归纳为这样的一个流水线,包含五个关键步骤: 1.JavaScript:一般来说,我们会使用JavaS ...
- OpenGL渲染流水线
其实OpenGL的流水线,对我学习来说只能算是一个概念性的东西.毕竟OpenGL也在发展,流水线也不会是一成不变的. 不过理解流水线的过程,重点在于理解每一步的作用,进而可以如何衔接起来,完成整个绘制 ...
- UML和模式应用4:初始阶段(4)--需求制品之用例模型模板示例
1. 前言 UP开发包括四个阶段:初始阶段.细化阶段.构建阶段.移交阶段: UP每个阶段包括 业务建模.需求.设计等科目: 其中需求科目对应的需求制品包括:设想.业务规则.用例模型.补充性规格说明.词 ...
随机推荐
- C#学习:构建一个更真实的基于LLM的简历评估系统
前言 昨天的Demo以txt文件为例进行说明,并且评估标准写死了,跟真实的简历评估系统差别太大了.今天分享的是经过改进后更加真实的基于LLM的简历评估系统. 使用AI生成了5份不同的简历,如下所示: ...
- 2023人形全能赛v831代码(包括YOLOv2识别和扫码以及颜色识别)
v831 import time, math from maix import nn, camera, display, image import serial class YOLOv2: def _ ...
- Axios 禁用缓存
Disable cache for Axios1. set headers.Cache-Control = 'no cache' // not work for 0.19.x or later?2. ...
- Java源码分析系列笔记-3.volatile
目录 1. 是什么 2. 什么情况 volatile 比 synchronized 更合适 2.1. 例子 2.2. 无法停止的原因分析 2.3. 解决方法 2.4. volatile vs sync ...
- Centos Yum源配置(清华源)
配置清华源 执行清华源官方语句https://mirrors.tuna.tsinghua.edu.cn/help/centos/?repo=centos7替换当前文件并备份 请注意,CentOS 8( ...
- ET5.0运行--基础
nodejs转c#,刚刚开始使用ET,在运行Demo中记录了一下. ET5.0 githu: https://github.com/egametang/ET/tree/Branch_V5.0 环境: ...
- ET框架运行初次--客户端资源更新(Mac环境)
1.首先在Mac上启动资源服务器.参考 https://www.cnblogs.com/cj8988/p/13965074.html 2.资源会下载到该位置( /Unity/Assets/Stream ...
- Blazor学习之旅 (13) Razor类库的使用
在上一篇我们学习了Blazor和JavaScript的互操作性,这一篇我们了解下如何创建和使用Razor类库. 什么是Razor类库? 我们都知道,在.NET应用程序中,我们可以通过NuGet来安装各 ...
- 你应该懂的AI大模型(十)之 LLamaFactory 之 LoRA微调Llama3
本文标题中说的微调 Llama3指的是局部微调,使用 LLamaFactory 局部微调 LIama3. 一.什么是LLamaFactory LLaMA-Factory 是一个开源的大型语言模型微调框 ...
- C# 读取类Class注释
https://www.cnblogs.com/shanfeng1000/p/14972515.html 友好的注释能提高代码的可读性,几乎所有的编程语言都支持注释. 在C#中,注释不是可执行代码的一 ...