官方关键UnityObjectToWorldNormal() 代码
从乐乐姐的书中得知,当我们在shader想获得法线,大概会这么些
o.wordDir = UnityObjectToWorldNormal(i.normal)
(这行代码就包含了官方对“unity_ObjectToWorld”的终极理解+极致应用)
也可以说,掌握了这行代码,就掌握了unity_ObjectToWorld
(这行代码的源码在楼下。。。)
这时候,肯定有一堆高人跳出来分析了, 这个很复杂,分两种情况,巴啦巴啦
我复杂个P,法线转世界坐标就复杂了??下面会一一展开说,甚至关键代码都是幼儿园数学,加减乘除而已
如下面代码,确实是if ... else .. ,也确实分两种“复杂”情况;
但是在大部分情况下,大部分的智商都差不多,所以都不要没搞懂一行代码逻辑,就if .. else ...去企图把两段代码都搞懂,反向一下逻辑很清楚的:如果两段代码是差不多,接近的,就会用一个方法传不同的参数解决。,而不是if .. else .. ;但既然用了if .. else ..解决, 也就是UnityObjectToWorldNormal()里的两段代码南辕北辙,
那么问题来了?你是希望:
1.先把第一段代码搞懂,(而省下了读第两段代码时间,以至于有经历集中完全掌握关键代码?),
2.还是头铁,两段一起搞,每一段都搞不通,而且两段互调一起又加深混乱?
如楼下这段代码,我们只需要了解, 根本不用管第二种情况(有兴趣同学可课外自行研究)
normalize(mul (unity_objecttoWorld,dir))//这个方法即可
未知代码1
首先,请看下面代码,看不懂没关系的,(未知1)后面会补上幼儿园数学,加减乘除而已,只要上过小学就没问题,初中辍学也能懂,
这段1代码主要是要计算_19的结果,而输入的参数_7,我们从 layout(location ) _7大概就知道这是:法线(normal)
//代码1
layout(location = 1) in vec3 _7;
layout(set = 1, binding = 0, std140) uniform _23_25
{
vec4 _m0[4];
vec4 _m1[4];
vec4 _m2[4];
vec4 _m3;
} _25;
void main()
{
_16.x = dot(_7, _25._m1[0u].xyz);
_16.y = dot(_7, _25._m1[1u].xyz);
_16.z = dot(_7, _25._m1[2u].xyz);
_19 = dot(_16.xyz, _16.xyz);
_19 = inversesqrt(_19);
}
未知代码2
因为未知1,所以我们需要未知2,还是不懂没关系的,请接着往下看,
(会越说越简单的)
//代码2
矢量 - 大概是这么一个东西
var pos = new Vector3(1, 1.5, 1);
矩阵 - 是大概这么一个东西(3个矩阵相乘)
根据某大牛说的:
矩阵:模型顶点从模型空间转换到世界空间用的矩阵,就是unity_ObjectToWorld,那么这个矩阵的内容是什么呢,没错,就是这个模型相对于世界空间原点的缩放,旋转和平移
矩阵最后一列代表的是模型中心点的世界坐标
M - local2World 的三列分别是:
1.最后一列,第4列代表的是模型中心点的世界坐标(明显的最后一列是tx,ty,tx,1)
2.另外第1,2, 3列后面再说
由于我们知道1列1列是有含义的(最后一列是世界坐标(真不需要说模型中心点的,世界坐标就是世界坐标,台湾省难道有任何可能会不是中国台湾省么))
我们知道了列的含义
所以楼上的代码_25[0u] _25[1u], _25[2u]就分别代表了,第1,2,3列,而不是代表行
(后面证明是错了,0,1,2就是行的列,_25行[0,1,2]列,
是:
m1[1] m1[2] 25[3]
m2
m2
而不是
m[1] m2 m3
m[2]
m[3]
那么。。。localToWorld矩阵的第1,2,3列(行)到底是什么意思呢?
最简单的解释localToWorld
_16.x = dot(_7, _25._m1[0u].xyz);
_16.y = dot(_7, _25._m1[1u].xyz);
_16.z = dot(_7, _25._m1[2u].xyz);
从楼上的代码(代码1)我们可知道
用法线 "_7" dot点乘 x 矩阵unity_objectToWorld "_25" 的第一列是什么意思呢
这里还做了3个dot
这里还做了3个dot
这里还做了3个dot
矩阵x 矢量(法线==矢量)数学含义
刚刚好,结果形成了新的矢量
//代码-2
点乘的数学含义
两个矢量的点乘,就是各自的分量.xyz相乘后,再相加
dot(vec a, vec b)= x_1x_2 + y_1y_2 + z_1z_2;
凑巧,就是,矩阵的第一行,3个数分别乘以矢量的x,y,z,结果就是dot点乘的结果
对应楼上的代码:就是那么刚好,刚刚同学遇到凑巧同学,所以,
//形成新的矢量 ——16
_16.x = dot(_7, _25._m1[0u].xyz);//如楼上伪代码,x==16
_16.y = dot(_7, _25._m1[1u].xyz);//如楼上伪代码,y==4
_16.z = dot(_7, _25._m1[2u].xyz);//如楼上伪代码,z==7
所以计算——16就是得出了法线——7的世界坐标(因为_7是从Localtion(2)声明,是从Vectex Shader传入,vertex为模型自身顶点坐标|法线也是自身坐标,还不是世界坐标)
未知代码3 - 法线归一
然后,这3行代码后面,还有2行代码:
下面——19这两行代码就太简单了,就是矢量的"归一化":
//形成新的矢量 ——16
_16.x = dot(_7, _25._m1[0u].xyz);//如楼上伪代码,x==16
_16.y = dot(_7, _25._m1[1u].xyz);//如楼上伪代码,y==4
_16.z = dot(_7, _25._m1[2u].xyz);//如楼上伪代码,z==7
//这两行代码就太简单了
_19 = dot(_16.xyz, _16.xyz);
_19 = inversesqrt(_19);
//法线的世界坐标的dir(_19)=nomalize(mul(法线,objectToWorld矩阵)
归一化是什么意思?我不打算解释了,总之上面代码
好了,说到这里,我们只需要掌握
- 矢量基本加减乘除,法线也是矢量
- 矩阵 =
- 矩阵相乘 = 3个,平移x旋转x缩放矩阵
- Shader基础语法
- 矩阵相乘后再乘以法线矢量
- 矢量归一化
等同于文章最开始的截图(调转)
“”_19“”法线的世界坐标的dir = nomalize(mul(法线, objectToWorld矩阵));
//objectToWorld矩阵也可以是float4x4的,但是最后一列前面解释了,没什么数学含义
//所以,也就是Unity官方的写法
worldDir = normalize(mul((float3x3)unity_ObjectToWorld, dir));
所以, 你掌握了官方的这个函数方法,就能准确应用 unity_ObjectToWorld ,当你会用了自然就明白了,只是unity 本身写的太绕(太基础了,谁都不想解释)
那么现在,你知道unity_ObjectToWorld矩阵,每行每列是什么意思了吧?
参考:
(2 条消息) unity_ObjectToWorld里的每一列分别代表什么意思? - 知乎 (zhihu.com)
UnityCG.cginc源码之UnityObjectToWorldNormal之模型非等比缩放导致的法线不垂直问题解决分析_shader法向量非等比缩放-CSDN博客