URP 空间转换
\(\begin{Bmatrix}模型空间(model\ space) \\对象空间(object\ space) \\局部空间(local\ space) \end{Bmatrix} \underbrace{\Rightarrow }_{模型变换} \begin{Bmatrix}世界空间(world\ space) \end{Bmatrix} \underbrace{\Rightarrow }_{观察变换} \begin{Bmatrix}观察空间(view\ space) \\摄像机空间(camera\ space) \\视图空间(view\ space) \end{Bmatrix} \underbrace{\Rightarrow}_{裁剪矩阵、投影矩阵} \begin{Bmatrix}裁剪空间(clip\ space) \\齐次裁剪空间 \end{Bmatrix} \Rightarrow \begin{Bmatrix}屏幕空间(screen\ space) \end{Bmatrix}\)
- 观察空间与屏幕空间是不同的,观察空间是一个三维空间,而屏幕空间是一个二维空间。从观察空间到屏幕空间的转换需要经过一个操作,那么就是投影(Projection)。
- 将顶点坐标从世界空间变换到观察坐标中,这个变换通常叫做观察变换(view transform)。
- 将顶点从观察空间变换到裁剪空间的矩阵叫做裁剪矩阵(clip matrix),也称投影矩阵(projection matrix)。
- 顶点着色器的最终目标需要返回一个已转换到裁剪空间的顶点坐标。
模型空间转世界空间
float3 TransformObjectToWorld(float3 positionOS)
世界空间转模型空间
float3 TransformWorldToObject(float3 positionWS)
世界空间转观察空间
float3 TransformWorldToView(float3 positionWS)
模型空间转齐次裁剪空间
float4 TransformObjectToHClip(float3 positionOS)
世界空间转齐次裁剪空间
float4 TransformWorldToHClip(float3 positionWS)
观察空间转齐次裁剪空间
float4 TransformWViewToHClip(float3 positionVS)
向量–模型空间转世界空间 第二个参数true 对结果进行归一化
float3 TransformObjectToWorldDir(float3 dirOS, bool doNormalize = true)
向量–世界空间转模型空间 第二个参数true 对结果进行归一化
float3 TransformWorldToObjectDir(float3 dirWS, bool doNormalize = true)
向量–世界空间转观察空间 第二个参数true 对结果进行归一化
real3 TransformWorldToViewDir(real3 dirWS, bool doNormalize = false)
向量–世界空间转齐次裁剪空间 第二个参数true 对结果进行归一化
real3 TransformWorldToHClipDir(real3 directionWS, bool doNormalize = false)
法线–模型空间转世界空间 第二个参数true 对结果进行归一化
float3 TransformObjectToWorldNormal(float3 normalOS, bool doNormalize = true)
法线–世界空间转模型空间 第二个参数true 对结果进行归一化
float3 TransformWorldToObjectNormal(float3 normalWS, bool doNormalize = true)
构造矩阵 返回TBN矩阵
real3x3 CreateTangentToWorld(real3 normal, real3 tangent, real flipSign)
切线空间转世界空间
real3 TransformTangentToWorld(real3 dirTS, real3x3 tangentToWorld)
世界空间转切线空间
real3 TransformWorldToTangent(real3 dirWS, real3x3 tangentToWorld)
切线空间转模型空间
real3 TransformTangentToObject(real3 dirTS, real3x3 tangentToWorld)
模型空间转切线空间
real3 TransformObjectToTangent(real3 dirOS, real3x3 tangentToWorld)
C#
本地坐标变换到世界:
Vector3 worldPosition = transform.parent.localToWorldMatrix.MultiplyPoint(transform.localPosition);
世界坐标转换到对应父节点下的坐标:
Vector3 localPosition = target.transform.worldToLocalMatrix.MultiplyPoint(worldPosition);
Unity URP Shader中使用的矩阵变换函数都在SpaceTransforms.hlsl头文件中
#ifndef UNITY_SPACE_TRANSFORMS_INCLUDED
#define UNITY_SPACE_TRANSFORMS_INCLUDED
// Return the PreTranslated ObjectToWorld Matrix (i.e matrix with _WorldSpaceCameraPos apply to it if we use camera relative rendering)
float4x4 GetObjectToWorldMatrix()
{
return UNITY_MATRIX_M;
}
float4x4 GetWorldToObjectMatrix()
{
return UNITY_MATRIX_I_M;
}
float4x4 GetWorldToViewMatrix()
{
return UNITY_MATRIX_V;
}
// Transform to homogenous clip space
float4x4 GetWorldToHClipMatrix()
{
return UNITY_MATRIX_VP;
}
// Transform to homogenous clip space
float4x4 GetViewToHClipMatrix()
{
return UNITY_MATRIX_P;
}
// This function always return the absolute position in WS
float3 GetAbsolutePositionWS(float3 positionRWS)
{
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
positionRWS += _WorldSpaceCameraPos;
#endif
return positionRWS;
}
// This function return the camera relative position in WS
float3 GetCameraRelativePositionWS(float3 positionWS)
{
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
positionWS -= _WorldSpaceCameraPos;
#endif
return positionWS;
}
real GetOddNegativeScale()
{
// FIXME: We should be able to just return unity_WorldTransformParams.w, but it is not
// properly set at the moment, when doing ray-tracing; once this has been fixed in cpp,
// we can revert back to the former implementation.
return unity_WorldTransformParams.w >= 0.0 ? 1.0 : -1.0;
}
float3 TransformObjectToWorld(float3 positionOS)
{
return mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)).xyz;
}
float3 TransformWorldToObject(float3 positionWS)
{
return mul(GetWorldToObjectMatrix(), float4(positionWS, 1.0)).xyz;
}
float3 TransformWorldToView(float3 positionWS)
{
return mul(GetWorldToViewMatrix(), float4(positionWS, 1.0)).xyz;
}
// Transforms position from object space to homogenous space
float4 TransformObjectToHClip(float3 positionOS)
{
// More efficient than computing M*VP matrix product
return mul(GetWorldToHClipMatrix(), mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)));
}
// Tranforms position from world space to homogenous space
float4 TransformWorldToHClip(float3 positionWS)
{
return mul(GetWorldToHClipMatrix(), float4(positionWS, 1.0));
}
// Tranforms position from view space to homogenous space
float4 TransformWViewToHClip(float3 positionVS)
{
return mul(GetViewToHClipMatrix(), float4(positionVS, 1.0));
}
// Normalize to support uniform scaling
float3 TransformObjectToWorldDir(float3 dirOS, bool doNormalize = true)
{
float3 dirWS = mul((float3x3)GetObjectToWorldMatrix(), dirOS);
if (doNormalize)
return SafeNormalize(dirWS);
return dirWS;
}
// Normalize to support uniform scaling
float3 TransformWorldToObjectDir(float3 dirWS, bool doNormalize = true)
{
float3 dirOS = mul((float3x3)GetWorldToObjectMatrix(), dirWS);
if (doNormalize)
return normalize(dirOS);
return dirOS;
}
// Tranforms vector from world space to view space
real3 TransformWorldToViewDir(real3 dirWS, bool doNormalize = false)
{
float3 dirVS = mul((real3x3)GetWorldToViewMatrix(), dirWS).xyz;
if (doNormalize)
return normalize(dirVS);
return dirVS;
}
// Tranforms vector from world space to homogenous space
real3 TransformWorldToHClipDir(real3 directionWS, bool doNormalize = false)
{
float3 dirHCS = mul((real3x3)GetWorldToHClipMatrix(), directionWS).xyz;
if (doNormalize)
return normalize(dirHCS);
return dirHCS;
}
// Transforms normal from object to world space
float3 TransformObjectToWorldNormal(float3 normalOS, bool doNormalize = true)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return TransformObjectToWorldDir(normalOS, doNormalize);
#else
// Normal need to be multiply by inverse transpose
float3 normalWS = mul(normalOS, (float3x3)GetWorldToObjectMatrix());
if (doNormalize)
return SafeNormalize(normalWS);
return normalWS;
#endif
}
// Transforms normal from world to object space
float3 TransformWorldToObjectNormal(float3 normalWS, bool doNormalize = true)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return TransformWorldToObjectDir(normalWS, doNormalize);
#else
// Normal need to be multiply by inverse transpose
float3 normalOS = mul(normalWS, (float3x3)GetObjectToWorldMatrix());
if (doNormalize)
return SafeNormalize(normalOS);
return normalOS;
#endif
}
real3x3 CreateTangentToWorld(real3 normal, real3 tangent, real flipSign)
{
// For odd-negative scale transforms we need to flip the sign
real sgn = flipSign * GetOddNegativeScale();
real3 bitangent = cross(normal, tangent) * sgn;
return real3x3(tangent, bitangent, normal);
}
real3 TransformTangentToWorld(real3 dirTS, real3x3 tangentToWorld)
{
// Note matrix is in row major convention with left multiplication as it is build on the fly
return mul(dirTS, tangentToWorld);
}
// This function does the exact inverse of TransformTangentToWorld() and is
// also decribed within comments in mikktspace.h and it follows implicitly
// from the scalar triple product (google it).
real3 TransformWorldToTangent(real3 dirWS, real3x3 tangentToWorld)
{
// Note matrix is in row major convention with left multiplication as it is build on the fly
float3 row0 = tangentToWorld[0];
float3 row1 = tangentToWorld[1];
float3 row2 = tangentToWorld[2];
// these are the columns of the inverse matrix but scaled by the determinant
float3 col0 = cross(row1, row2);
float3 col1 = cross(row2, row0);
float3 col2 = cross(row0, row1);
float determinant = dot(row0, col0);
float sgn = determinant<0.0 ? (-1.0) : 1.0;
// inverse transposed but scaled by determinant
// Will remove transpose part by using matrix as the first arg in the mul() below
// this makes it the exact inverse of what TransformTangentToWorld() does.
real3x3 matTBN_I_T = real3x3(col0, col1, col2);
return SafeNormalize( sgn * mul(matTBN_I_T, dirWS) );
}
real3 TransformTangentToObject(real3 dirTS, real3x3 tangentToWorld)
{
// Note matrix is in row major convention with left multiplication as it is build on the fly
real3 normalWS = TransformTangentToWorld(dirTS, tangentToWorld);
return TransformWorldToObjectNormal(normalWS);
}
real3 TransformObjectToTangent(real3 dirOS, real3x3 tangentToWorld)
{
// Note matrix is in row major convention with left multiplication as it is build on the fly
// don't normalize, as normalWS will be normalized after TransformWorldToTangent
float3 normalWS = TransformObjectToWorldNormal(dirOS, false);
// transform from world to tangent
return TransformWorldToTangent(normalWS, tangentToWorld);
}
#endif