【渲染流水线】[几何阶段]-[屏幕映射]以UnityURP为例

  • 屏幕映射阶段是将NDC三维空间坐标转换为最终屏幕像素位置的关键步骤。
  • 此阶段由GPU固定管线完成,开发者不可编程,仅可通过视口设置调整映射范围

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

NDC坐标范围

  • NDC空间是裁剪空间经过透视除法(除以w分量)后得到的归一化立方体,坐标范围为:
  • x, y轴:[-1, 1](左下角为(-1,-1),右上角为(1,1)
  • z轴:[0, 1](深度值,0为近裁剪面,1为远裁剪面)

视口变换(屏幕映射)

  • 将NDC坐标映射到屏幕空间:
  • x轴 ‌:将[-1, 1]线性映射到[0, 屏幕宽度]
    • xScreen=(xNDC+1)×0.5×屏幕宽度
  • y轴 ‌:因屏幕坐标系原点在左上角,需反转y轴(1 - NDC_y)再映射到[0, 屏幕高度]
    • yScreen=(1−yNDC)×0.5×屏幕高度
  • 保留z_{ndc}用于深度测试‌

URP中的具体映射案例‌

  • 假设屏幕分辨率为1920×1080,一个NDC坐标为(0, 0, 0.5)
  • x轴映射 ‌:Screen_x = (0 + 1)/2 × 1920 = 960
  • y轴映射 ‌:Screen_y = (1 - 0)/2 × 1080 = 540(因y轴反转,NDC的0对应屏幕顶部)
  • 结果 ‌:该点位于屏幕正中央像素(960, 540)

‌技术细节说明‌

  • 视口调整 ‌:可通过UnityEngine.Rendering.CommandBuffer.SetViewport修改映射区域(如画中画效果),默认映射全屏。
  • 与裁剪空间关系 ‌:顶点着色器需输出齐次裁剪坐标(如UnityObjectToClipPos(v.vertex)),GPU自动执行透视除法和屏幕映射。
  • 精度影响‌:高分辨率下浮点精度误差可能引起像素偏移,需注意抗锯齿设置。
glsl 复制代码
//计算点在屏幕空间中的位置:首先positionCS中的值还没有进行其次除法,需要先除以positionCS.w转换为
//NDC坐标,范围[-1,1],然后*0.5+0.5后转为[0,1],最后乘以_ScreenParams转换为最终的屏幕空间位置
float2 p0 = _ScreenParams.xy * (p[0].positionCS.xy / p[0].positionCS.w * 0.5 + 0.5);

几何阶段完整流程及数据变换示例‌

初始坐标 ‌:模型空间顶点坐标 (2.0, 1.0, -3.0, 1.0)(齐次坐标w=1),摄像机使用透视投影,近平面z=0.3,远平面z=1000,视口分辨率1920x1080。

‌1. 顶点着色器(Vertex Shader)‌

  • 输入 ‌:模型空间坐标 (2.0, 1.0, -3.0, 1.0)
  • 操作 ‌:应用MVP矩阵(Model-View-Projection)变换到齐次裁剪空间。假设变换后输出为:(x ′,y ′,z ′,w′)=(1.5,0.8,−2.2,3.0)
  • 输出 ‌:齐次裁剪坐标 (1.5, 0.8, -2.2, 3.0)

‌2. 曲面细分着色器(可选,Tessellation Shader)‌

  • 输入‌:若启用细分,接收控制点数据(如原始三角形顶点)。
  • 操作 ‌:细分生成新顶点,例如输出新增顶点 (1.2, 0.6, -2.0, 2.8)
  • 输出‌:细分后的齐次裁剪坐标(假设未启用,跳过此阶段)

‌3. 几何着色器(可选,Geometry Shader)‌

  • 输入‌:图元顶点(如三角形三个顶点)。
  • 操作‌:可生成新图元(如将三角形拆分为多个线段)。若未启用,直接传递数据。
  • 输出‌:原始或新增的齐次裁剪坐标(假设未启用,跳过此阶段)

‌4. 图元装配(Primitive Assembly)‌

  • 输入 ‌:齐次裁剪坐标 (1.5, 0.8, -2.2, 3.0)(假设为三角形的一个顶点)。
  • 操作‌:将顶点组装为图元(如三角形),并计算包围盒。
  • 输出‌:完整的三角形图元(三个顶点数据)

‌5. 视锥体裁剪(Frustum Culling)‌

  • 输入 ‌:齐次裁剪坐标 (1.5, 0.8, -2.2, 3.0)
  • 操作 ‌:
    • 透视除法 ‌:转换为NDC坐标 (1.5/3.0, 0.8/3.0, -2.2/3.0) = (0.5, 0.267, -0.733)
    • 边界检查‌:若NDC坐标超出[-1,1]范围则裁剪。本例中x/y在范围内,但z=-0.733(OpenGL下有效,DirectX需z∈[0,1]则剔除)。
    • 插值裁剪‌:若部分顶点超出边界,生成新顶点(如裁剪z边界时插值计算新坐标)。
  • 输出 ‌:裁剪后的NDC坐标(假设全部保留) (0.5, 0.267, -0.733)

‌6. 屏幕映射(Screen Mapping)‌

  • 输入 ‌:NDC坐标 (0.5, 0.267, -0.733)
  • 操作 ‌:
    • 视口变换 ‌:将NDC的xy映射到屏幕像素坐标(如1920x1080):
      • xscreen=(0.5+1)∗1920/2=1440
      • yscreen=(0.267+1)∗1080/2=684.18
    • 深度保留 ‌:z值转换为深度缓冲值(如OpenGL下 (-0.733+1)/2=0.1335)。
  • 输出 ‌:屏幕空间坐标 (1440, 684, 0.1335)

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

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

相关推荐
在路上看风景1 天前
26. Mipmap
unity
咸鱼永不翻身1 天前
Unity视频资源压缩详解
unity·游戏引擎·音视频
在路上看风景1 天前
4.2 OverDraw
unity
在路上看风景1 天前
1.10 CDN缓存
unity
ellis19701 天前
Unity插件SafeArea Helper适配异形屏详解
unity
nnsix2 天前
Unity Physics.Raycast的 QueryTriggerInteraction枚举作用
unity·游戏引擎
地狱为王2 天前
Cesium for Unity叠加行政区划线
unity·gis·cesium
小贺儿开发2 天前
Unity3D 八大菜系连连看
游戏·unity·互动·传统文化
在路上看风景2 天前
25. 屏幕像素和纹理像素不匹配
unity
ۓ明哲ڪ2 天前
Unity功能——创建新脚本时自动添加自定义头注释
unity·游戏引擎