【渲染流水线】[几何阶段]-[屏幕映射]以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开始探索游戏渲染】专栏-直达

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

相关推荐
旧物有情18 小时前
游戏开发常用架构 #MVP,MVC
游戏·unity·架构·mvc
勇踏前人未索之境20 小时前
Unity打包运行于鸿蒙手机
unity·智能手机·harmonyos
玖玥拾21 小时前
Unity 3D 笔记(十一)UI 框架进阶:栈弹窗交互、BasePanel 基类虚方法、DoTween 界面动画
笔记·3d·unity
yueqc11 天前
Android 渲染(三):掉帧监控
android·渲染·apm·掉帧
郝学胜-神的一滴1 天前
中级OpenGL教程 023:Assimp模型加载全解——从源码到架构的骈文探秘
c++·unity·游戏引擎·cmake·unreal engine·opengl
xcLeigh2 天前
Unity基础:GameObject与Component——Unity核心架构思想彻底理解
unity·教程·component·gameobject
郝学胜-神的一滴2 天前
中级OpenGL教程 022:探秘三维世界的血脉传承——物体父子关系与矩阵递归奥义
c++·线性代数·unity·矩阵·游戏引擎·unreal engine·opengl
weixin_424294673 天前
Unity的测试Edit Mode和Play Mode,有什么区别?
unity
帅次3 天前
Android 高级工程师面试:Flutter 渲染与性能 近1年高频追问 20 题
android·flutter·面试·渲染·性能