一,原理
整理这篇文章的原因是,前段时间项目主要用的是CG语言,最近项目用的是HLSL语言。为什么图形这块会转HLSL语言呢? 其实是CG语言已经很久不更新了,且垮平台压力比较大;HLSL是微软DX着色器汇编语言(当然也不完全一样),HLSL其实并不能与OpenGL兼容,但这也难不倒Unity(咱们可以编译,哈哈 )。言归正传,咱们线看一张图:
空间坐标系转换原理 :
注意:一个顶点坐标从模型到最终渲染到屏幕中需要经过顶点变换(顶点要经过多个坐标空间的转化常能映射到屏幕上)。
模型空间:以模型原点为参照物,当模型Transform改变的时候,模型空间也会随之改变,模型空间又被称为对象空间或局部空间。
世界空间:世界空间是游戏中最大的坐标空间(好比现实世界的经纬度和高度)。世界空间是基于世界原点坐标(0,0,0),为游戏空间提供一个绝对位置。
观察空间(摄像机空间):摄像机为原点,为游戏提供渲染视角。 Unity 的观察空间是 左手坐标系(正X指向右方,正Y指向上方,正Z轴指向摄像机后方)。
裁剪空间:对处于该空间内的图元进行渲染(裁剪),不处于该空间的图元会被剔除。裁剪区域是摄像机是视锥体决定(视锥体就是摄像机所看见的区域范围,它由六个平面包围。这些平面被称为裁剪平面。视锥体被分为两类:A,正交投影;B,透视投影)。
屏幕空间:最终显示到屏幕的空间,这里我们会得到像素位置,屏幕空间是2维坐标系。
二,坐标系
世界坐标系(World Space)
按照笛卡尔坐标系定义出来的绝对坐标系(空间直角坐标系)。左手坐标系获取方法:Transform.Position
本地坐标系(Local Space)====> 模型坐标系
模型自生坐标。美术建模即创建好,Unity无法修改坐标原点。在shader中默认输入的就是模型坐标 ,获取方法:Transform.LocalPosition
屏幕坐标系(Screen Space)
以屏幕作为坐标,左下角为(0,0)
观察坐标系(ViewPort Space):视口坐标系
摄像机即观察视角,将画面投影到该视口上,左下角为原点(0,0),右上角为(1,1)
裁剪坐标 (Clip Space)
对处于该空间内的图元进行渲染(裁剪),不处于该空间的图元会被剔除。
UI坐标系
UI坐标系是可视化窗口的最顶层,世界坐标系的物体都会被UI坐标系遮挡 .
UV坐标系
uv坐标系只负责定位贴图和模型的关系
三,坐标系转化
四,常见的矩阵
2.1 平移矩阵
float4x4 M_translate = float4x4(
1, 0, 0, T.x,
0, 1, 0, T.y,
0, 0, 1, T.z,
0, 0, 0, 1);
2.2 缩放矩阵
float4x4 M_scale = float4x4(
S.x, 0, 0, 0,
0, S.y, 0, 0,
0, 0, S.z, 0,
0, 0, 0, 1);
旋转矩阵(X轴)
float4x4 M_rotationX = float4x4(
1, 0, 0, 0,
0, cos(θ), -sin(θ), 0,
0, sin(θ), cos(θ), 0,
0, 0, 0, 1);
旋转矩阵(Y轴)
float4x4 M_rotationY = float4x4(
cos(θ), 0, sin(θ), 0,
0, 1, 0, 0,
-sin(θ), 0, cos(θ), 0,
0, 0, 0, 1);
旋转矩阵(Z轴)
float4x4 M_rotationZ = float4x4(
cos(θ), -sin(θ), 0, 0,
sin(θ), cos(θ), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
将P点从A空间变换到B空间
五,空间转化函数 (代码左右拉可以看CG和 HLSL函数对应)
//顶点这色器
Varyings o;
CGPROGRAM 语言 HLSLPROGRAM 语言
*****顶点相关
//模型空间转为裁剪空间
o.vec = UnityObjectToClipPos(v.vertex); TransformObjectToHClip(positionOS ) ;
o.vec = UnityObjectToClipPos(float4(v.vertex), 1.0));
//模型空间转换为观察空间
o.vec = UnityObjectToViewPos(v.vertex); TransformObjectToWorld(positionOS) ;
o.vec = mul(UNITY_MATRIX_MV, float4(v.vertex), 1.0)).xyz;
//从世界空间转到观察空间 //通过左乘UNITY_MATRIX_V矩阵
o.vec = UnityWorldToViewPos(v.vertex); TransformWorldToView(positionOS) ;
//模型空间转换到世界空间 //通过左乘UNITY_MATRIX_M矩阵
o.vec = UnityObjectToWorldDir(v.vertex); TransformObjectToWorld(positionOS) ;
//观察空间转为裁剪空间
o.vec = UnityViewToClipPos(v.vertex); float4 TransformWViewToHClip(float3 positionVS) ;
//世界空间转到裁剪空间
o.vec = UnityWorldToClipPos(v.vertex); TransformWorldToHClip(positionWS) ;
// 世界空间转化为模型空间 //通过左乘UNITY_MATRIX_M矩阵
o.vec = UnityWorldToObject(v.vertex); TransformWorldToObject(positionWS) ;
*****法线相关
//法线从模型空间转换到世界空间 //这里使用右乘的数学意义是,非统一缩放的情况下,直接左乘变换矩阵法线方向会被改动,
o.normal = UnityObjectToWorldNormal(v.vertex); //所以需要使用的是变换矩阵中缩放矩阵的逆矩阵,进行左乘。
//又有左乘矩阵和右乘矩阵的转置矩阵是等价的,而旋转矩阵是正交矩阵,其逆矩阵等价于转置矩阵。
//所以这里使用变换/矩阵的逆矩阵右乘法线,来抵消非统一缩放产生的影响.
TransformObjectToWorldNormal(normalOS);
// 法线–模型空间转世界空间 第二个参数true 对结果进行归一化
float3 TransformObjectToWorldNormal(float3 normalOS, bool doNormalize = true)
//世界空间转到模型空间 // 法线–世界空间转模型空间 第二个参数true 对结果进行归一化
o.normal = UnityWorldToObjectDir(v.vertex); float3 TransformWorldToObjectNormal(float3 normalWS, bool doNormalize = true)
*****切线相关
//通过右乘世界空间到切线空间的变换矩阵完成逆变换,
//把切线空间的方向矢量变换到世界空间
TransformTangentToWorld(tangenOS) ;
//构建矩阵 切线空间转世界空间
real3 TransformTangentToWorld(real3 dirTS, real3x3 tangentToWorld)
//世界空间转切线空间
real3 TransformWorldToTangent(real3 dirWS, real3x3 tangentToWorld);
//切线空间转模型空间
real3 TransformTangentToObject(real3 dirTS, real3x3 tangentToWorld);
//模型空间转切线空间
real3 TransformObjectToTangent(real3 dirOS, real3x3 tangentToWorld);
*****摄像机相关
//输入一个object空间顶点位置,返回世界空间中从该点到摄像机的观察方向。
o.camPos = WorldSpaceViewDir(v.vertex);
//输入一个object空间顶点位置,返回object空间中从该点到摄像机的观察方向。
o.camPos = ObjSpaceViewDir (v.vertex);
*****灯光相关
//仅用于前向渲染,输入一个object空间顶点的位置,返回世界空间中从该点到光源的光照方向 //通过左乘UNITY_MATRIX_M矩阵,把方向矢量从模型空间变换到世界空间,
o.LigtDir = WorldSpaceLightDir (v.vertex); //做了一次归一化以支持统一缩放 ,
//仅用于前向渲染,输入一个object空间顶点的位置,返回object空间中从该点到光源的光照方向 // 第二个参数用于判断是否进行归一化 .
o.LigtDir = ObjSpaceLightDir (v.vertex); TransformObjectToWorldDir(Direction, true) ;
//通过左乘UNITY_MATRIX_I_M矩阵,把方向矢量从世界空间变换到模型空间,
//做了一次归一化以支持统一缩放 ,
// 第二个参数用于判断是否进行归一化
TransformWorldToObjectDir(Direction, true) ;
*****求物体与摄像机距离
//先将坐标转换至裁剪空间
float3 viewPos=UnityObjectToClipPos(v.vertex);
//物体(顶点)与镜头距离
float distance=length(viewPos);
*****求物体(顶点)与摄像机的方向:
//先将坐标从模型空间转为世界空间
float3 worldPos=UnityObjectToWorldDir(v.vertex);
//物体(顶点)与摄像机的方向
o.viewDir=normalize(UnityWorldSpaceViewDir(worldPos));
return o;
}
*****向量相关
//向量–模型空间转世界空间 第二个参数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)
1,CG:空间转化函数
CGPROGRAM 语言
*****顶点相关
//模型空间转为裁剪空间
o.vec = UnityObjectToClipPos(v.vertex);
o.vec = UnityObjectToClipPos(float4(v.vertex), 1.0));
//模型空间转换为观察空间
o.vec = UnityObjectToViewPos(v.vertex);
o.vec = mul(UNITY_MATRIX_MV, float4(v.vertex), 1.0)).xyz;
//从世界空间转到观察空间
o.vec = UnityWorldToViewPos(v.vertex);
//模型空间转换到世界空间
o.vec = UnityObjectToWorldDir(v.vertex);
//观察空间转为裁剪空间
o.vec = UnityViewToClipPos(v.vertex);
//世界空间转到裁剪空间
o.vec = UnityWorldToClipPos(v.vertex);
// 世界空间转化为模型空间
o.vec = UnityWorldToObject(v.vertex);
*****法线相关
//法线从模型空间转换到世界空间
o.normal = UnityObjectToWorldNormal(v.vertex);
//世界空间转到模型空间
o.normal = UnityWorldToObjectDir(v.vertex);
*****切线相关
*****摄像机相关
//输入一个object空间顶点位置,返回世界空间中从该点到摄像机的观察方向。
o.camPos = WorldSpaceViewDir(v.vertex);
//输入一个object空间顶点位置,返回object空间中从该点到摄像机的观察方向。
o.camPos = ObjSpaceViewDir (v.vertex);
*****灯光相关
//仅用于前向渲染,输入一个object空间顶点的位置,返回世界空间中从该点到光源的光照方向
o.LigtDir = WorldSpaceLightDir (v.vertex);
//仅用于前向渲染,输入一个object空间顶点的位置,返回object空间中从该点到光源的光照方向
o.LigtDir = ObjSpaceLightDir (v.vertex);
*****求物体与摄像机距离
//先将坐标转换至裁剪空间
float3 viewPos=UnityObjectToClipPos(v.vertex);
//物体(顶点)与镜头距离
float distance=length(viewPos);
*****求物体(顶点)与摄像机的方向:
//先将坐标从模型空间转为世界空间
float3 worldPos=UnityObjectToWorldDir(v.vertex);
//物体(顶点)与摄像机的方向
o.viewDir=normalize(UnityWorldSpaceViewDir(worldPos));
return o;
}
2,HLSL:空间转化函数
HLSLPROGRAM 语言
*****顶点相关
//模型空间转为裁剪空间
TransformObjectToHClip(positionOS ) ;
//模型空间转换为观察空间
TransformObjectToWorld(positionOS) ;
//从世界空间转到观察空间 通过左乘UNITY_MATRIX_V矩阵
TransformWorldToView(positionOS) ;
//模型空间转换到世界空间 通过左乘UNITY_MATRIX_M矩阵
TransformObjectToWorld(positionOS) ;
//观察空间转为裁剪空间
float4 TransformWViewToHClip(float3 positionVS) ;
//世界空间转到裁剪空间
TransformWorldToHClip(positionWS) ;
// 世界空间转化为模型空间 通过左乘UNITY_MATRIX_M矩阵
TransformWorldToObject(positionWS) ;
*****法线相关
//法线从模型空间转换到世界空间 //这里使用右乘的数学意义是,非统一缩放的情况下,直接左乘变换矩阵法线方向会被改动,
//所以需要使用的是变换矩阵中缩放矩阵的逆矩阵,进行左乘。
//又有左乘矩阵和右乘矩阵的转置矩阵是等价的,而旋转矩阵是正交矩阵,其逆矩阵等价于转置矩阵。
//所以这里使用变换/矩阵的逆矩阵右乘法线,来抵消非统一缩放产生的影响.
TransformObjectToWorldNormal(normalOS);
// 法线–模型空间转世界空间 第二个参数true 对结果进行归一化
float3 TransformObjectToWorldNormal(float3 normalOS, bool doNormalize = true)
//世界空间转到模型空间 法线–世界空间转模型空间 第二个参数true 对结果进行归一化
float3 TransformWorldToObjectNormal(float3 normalWS, bool doNormalize = true)
*****切线相关
//通过右乘世界空间到切线空间的变换矩阵完成逆变换,
//把切线空间的方向矢量变换到世界空间
TransformTangentToWorld(tangenOS) ;
//构建矩阵 切线空间转世界空间
real3 TransformTangentToWorld(real3 dirTS, real3x3 tangentToWorld)
//世界空间转切线空间
real3 TransformWorldToTangent(real3 dirWS, real3x3 tangentToWorld);
//切线空间转模型空间
real3 TransformTangentToObject(real3 dirTS, real3x3 tangentToWorld);
//模型空间转切线空间
real3 TransformObjectToTangent(real3 dirOS, real3x3 tangentToWorld);
*****摄像机相关
//输入一个object空间顶点位置,返回世界空间中从该点到摄像机的观察方向。
o.camPos = WorldSpaceViewDir(v.vertex);
//输入一个object空间顶点位置,返回object空间中从该点到摄像机的观察方向。
o.camPos = ObjSpaceViewDir (v.vertex);
*****灯光相关
//通过左乘UNITY_MATRIX_M矩阵,把方向矢量从模型空间变换到世界空间,
//做了一次归一化以支持统一缩放 ,
// 第二个参数用于判断是否进行归一化 .
TransformObjectToWorldDir(Direction, true) ;
//通过左乘UNITY_MATRIX_I_M矩阵,把方向矢量从世界空间变换到模型空间,
//做了一次归一化以支持统一缩放 ,
// 第二个参数用于判断是否进行归一化
TransformWorldToObjectDir(Direction, true) ;
*****求物体与摄像机距离
//先将坐标转换至裁剪空间
float3 viewPos=UnityObjectToClipPos(v.vertex);
//物体(顶点)与镜头距离
float distance=length(viewPos);
*****求物体(顶点)与摄像机的方向:
//先将坐标从模型空间转为世界空间
float3 worldPos=UnityObjectToWorldDir(v.vertex);
//物体(顶点)与摄像机的方向
o.viewDir=normalize(UnityWorldSpaceViewDir(worldPos));
return o;
}
*****向量相关
//向量–模型空间转世界空间 第二个参数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)