在Shader编写过程中,常常需要处理不同坐标系下的顶点位置信息,因此必须借助MVP矩阵来实现相应的坐标变换操作。在Unity引擎中,不仅为用户提供了完备的各类MVP矩阵及其逆矩阵,还给出了历史帧中的矩阵数据。

坐标系转换函数

坐标转换函数

原理:补齐第四维度w1.0,应用对应矩阵,返回XYZ

局部空间 ⇔ 世界空间

HLSL
float3 TransformObjectToWorld(float3 positionOS) // 局部空间 ⇒ 世界空间
float3 TransformWorldToObject(float3 positionWS) // 世界空间 ⇒ 局部空间
点击展开查看更多

观察空间 ⇔ 世界空间

HLSL
float3 TransformViewToWorld(float3 positionVS) // 观察空间 ⇒ 世界空间
float3 TransformWorldToView(float3 positionWS) // 世界空间 ⇒ 观察空间
点击展开查看更多

任意空间 ⇔ 世界空间

HLSL
float4 TransformObjectToHClip(float3 positionOS) // 局部空间 ⇒ 裁剪空间
float4 TransformWorldToHClip(float3 positionWS) // 世界空间 ⇒ 裁剪空间
float4 TransformWViewToHClip(float3 positionVS) // 观察空间 ⇒ 裁剪空间
点击展开查看更多

其他函数

  • 方向向量转换函数
    • 原理:将变换矩阵的第四维度丢弃,转为3x3矩阵,再将其应用。
    • 定义real3 Transform###To###Dir(real3 dir, bool doNormalize)
    • 参数:局部空间 与 世界空间互转的函数中doNormalize参数默认值为true,且参数和返回值的类型均为float3
  • 法线转换函数
  • 更多请查看:库文件 Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl

矩阵变量

模型矩阵 Model Matrix

  • 原矩阵UNITY_MATRIX_MGetObjectToWorldMatrix()unity_ObjectToWorld(旧)
  • 逆矩阵UNITY_MATRIX_I_MGetWorldToObjectMatrix()unity_WorldToObject(旧)
  • 历史帧数据
    • 原矩阵UNITY_PREV_MATRIX_MGetPrevObjectToWorldMatrix()
    • 逆矩阵UNITY_PREV_MATRIX_I_MGetPrevWorldToObjectMatrix()

历史帧数据表示​​上一帧​​的模型空间到世界空间的变换矩阵。它存储了前一帧的变换状态,主要用于处理与时间相关的效果(如运动模糊、顶点动画插值等)。

视图矩阵 View Matrix

  • 原矩阵UNITY_MATRIX_VGetWorldToViewMatrix()unity_WorldToCamera(旧)
  • 逆矩阵UNITY_MATRIX_I_VGetViewToWorldMatrix()unity_CameraToWorld(旧)

投影矩阵 Project Matrix

  • 矩阵UNITY_MATRIX_PGetViewToHClipMatrix()
  • 逆矩阵UNITY_MATRIX_I_P

视图矩阵 ✕ 投影矩阵

  • 原矩阵UNITY_MATRIX_VPGetWorldToHClipMatrix()
  • 逆矩阵UNITY_MATRIX_I_VP

坑:新旧两版视图矩阵的区别

在视图矩阵中,UNITY_MATRIX_V/UNITY_MATRIX_I_V 与 unity_WorldToCamera/unity_CameraToWorld 的值是不一样的! 直接看Unity源码中unity_WorldToCamera/unity_CameraToWorld的赋值代码:

CS
Matrix4x4 worldToCameraMatrix = Matrix4x4.Scale(new Vector3(x: 1.0f, y: 1.0f, z: -1.0f)) * viewMatrix;
Matrix4x4 cameraToWorldMatrix = worldToCameraMatrix.inverse;
cmd.SetGlobalMatrix(ShaderPropertyId.worldToCameraMatrix, worldToCameraMatrix);
cmd.SetGlobalMatrix(ShaderPropertyId.cameraToWorldMatrix, cameraToWorldMatrix);
点击展开查看更多

我们可以看到unity_WorldToCamera/unity_CameraToWorld实际上是在视图矩阵viewMatrix的基础上将Z轴反向,而UNITY_MATRIX_V/UNITY_MATRIX_I_V并不会进行任何修改。 在Unity中,相机空间通常是一个左手坐标系,由此我们便可知,UNITY_MATRIX_V/UNITY_MATRIX_I_V的相机空间保留了原来的左手系,unity_WorldToCamera/unity_CameraToWorld的相机空间被转成了右手坐标系,而Unity的世界空间是一个右手坐标系,这意味着如果你直接使用UNITY_MATRIX_V/UNITY_MATRIX_I_V在某些情况下(如转换深度图重建的世界坐标)可能会出现问题。如果出现问题可以将欲转换的坐标的Z轴反向后再转换:

HLSL
pixelPosVS.z *= -1.0;
float3 pixelPosWS = TransformViewToWorld(pixelPosVS);
点击展开查看更多

版权声明

作者: Cheyne Xie

链接: https://chaim.eu.org/posts/300b4bc6/

许可证: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键