做網(wǎng)站有發(fā)票嗎站外推廣怎么做
這里介紹一個小TIPS,很久沒有這么有成就感了。我以前在學3D數(shù)學的時候,書上就有一句話,說你把矢量這些東西用久了,就應該形成一種“直覺”,仿佛這些東西就是你的左右手一樣。而這次,我居然真的用“直覺”來解決問題了,可以說是“瞎貓碰到死耗子”式的解決問題方法:
(一)問題起因
我現(xiàn)在在寫的是一個軟件光柵化的引擎。在軟件光柵化的階段,有兩個地方可以進行光照渲染,一個是在世界空間下進行,一個是在相機空間下進行。在世界空間下進行會很方便,因為光照并不涉及旋轉(zhuǎn),而只是涉及平移。但是,因為矩陣乘法可以結(jié)合,所以從物體空間變換到世界空間再變換到相機空間,這兩個矩陣可以合二為一,因為我是寫軟件光柵化,所以節(jié)約一些算力是有必要的。
?(二)矢量旋轉(zhuǎn)的問題
我有一個平行光。平行光是沒有距離的,只有方向,這個方向用一個矢量(像Vector3(1,1,1)這樣)。但在旋轉(zhuǎn)中有兩個問題:
1、矢量的旋轉(zhuǎn)不像點的旋轉(zhuǎn)。頂點是可以通過矩陣進行旋轉(zhuǎn)的,但是矢量不行。雖然在數(shù)學上矢量和點都是等價的。但是因為旋轉(zhuǎn)屬于非線性變換,所以最終在數(shù)學上的結(jié)果(歸一化)之后,這個矢量方向其實不是你想要的矢量。
2、從世界空間到相機空間的旋轉(zhuǎn),其實是一個旋轉(zhuǎn)的逆運算。也就是說,相機的Rotation,不是將世界空間按這個Rotation旋轉(zhuǎn),而是要做逆旋轉(zhuǎn)。
(三)解決的過程
當時我想了一些歪門邪道。開始的時候我嘗試用兩個點記錄位置。比如將矢量轉(zhuǎn)化為P1和P2。然后使用這P1和P2進行相機旋轉(zhuǎn),因為P1和P2就是兩個點,點旋轉(zhuǎn)之后,之間的位置關(guān)系是不變的。在旋轉(zhuǎn)之后,再取這兩個點計算矢量方向。理論上應該可行,但不知道為什么失敗了。
后來我將角度反轉(zhuǎn),然后進行四元數(shù)的矢量旋轉(zhuǎn)乘法,四元數(shù)的矢量乘法公式如下:
V=要變換的矢量(要進行齊次變化,W設置為0),Q=旋轉(zhuǎn)的四元數(shù),Q-1=四元數(shù)的逆
V = Q * V * (Q-1)
沒到想居然成功了。這樣得到的矢量居然是可以用的。光照計算沒有問題。但是為什么我說瞎貓碰到死耗子?因為在我的變換矩陣中有一個BUG。相機變換矩陣是按照XYZ三個軸進行變換的。我使用的是Unity的規(guī)則,按照Z-X-Y(Roll-Pitch-Yaw)的順序變換。我在寫相機變換矩陣的時候,也是這么寫的。但是我忘記了,相機變換是一個逆變換,其實應該寫成Y-X-Z變換的。其實先后順序只是規(guī)則的問題,你怎么寫,實際變換上也不會出錯。所以我一開始沒看出這個BUG。
但是后來我在進行相機的旋轉(zhuǎn)控制的時候,我發(fā)現(xiàn)我的旋轉(zhuǎn)控制有問題:在進行了Yaw的旋轉(zhuǎn)之后,再使用Pitch會沿Y軸旋轉(zhuǎn),而不是在地平線旋轉(zhuǎn)。雖然在數(shù)學上這沒有問題,但做為玩家控制來說有大問題。這也是為什么Unity使用Z-X-Y旋轉(zhuǎn)順序的原因。
然后查出BUG之后,才知道是我的旋轉(zhuǎn)搞錯了。我就把旋轉(zhuǎn)改了。但這樣一改,原來那個我將角度反轉(zhuǎn)之后,再利用四元數(shù)旋轉(zhuǎn)的方法就失靈了,得到的是一個錯誤的結(jié)果。
(四)解決方法
這里就是靠“直覺”解決問題了。如果將角度反轉(zhuǎn)沒有作用,那么,將四元數(shù)的運算反轉(zhuǎn)呢?這是一個突發(fā)奇想,我在教程,書里面都沒有見過這種說法,所以我說“瞎貓碰到死耗子”,當然,這應該也有其它書里面說,只是我沒看到而已。但總之這個思路解決了問題。只需要將四元數(shù)旋轉(zhuǎn)的運算改變,就能夠得到一個能成功變換到相機空間的矢量:
使用逆運算公式:V = (Q-1) * V * Q
// 旋轉(zhuǎn)矢量
Vector3 RotateDirection(Vector direction, Quaternion rotation)
{Quaternion re(rotation.GetEular());Vector3 transDirection = re.RotateMulInverse(direction);return transDirection.Normalize();
}// 求四元數(shù)的逆
Quaternion Quaternion::GetInverse(void) const {// 這是四元數(shù)的長度// 因為四元數(shù)長度始終是單位1,所以其實這一步可以省,只要你確保它沒發(fā)生蠕變float len = Magnitude(); return Quaternion(-x / len, -y / len, -z / len, w / len);
}// 四元數(shù)的乘法(正常)
Vector3 Quaternion::RotateMul(const Vector3& v) const {Quaternion vq(v.x, v.y, v.z, 0.0);Quaternion r = *this * vq * this->GetInverse();return Vector3(r.x, r.y, r.z);
}// 四元數(shù)的乘法(逆向)
Vector3 Quaternion::RotateMulInverse(const Vector3& v) const {Quaternion vq(v.x, v.y, v.z, 0.0);Quaternion r = this->GetInverse() * vq * (*this);return Vector3(r.x, r.y, r.z);
}
OK。以上就是解決方案了。