C#DirectX3D開發(1) 攝像機旋轉問題,旋轉上下90度時出問題
問題描述:
當繞X軸旋轉時,從90度旋轉到-90度正常,再繼續旋轉時無法旋轉過去,不能達到360度旋轉,求高手賜教。
自己搜尋了,有人碰到相同問題:
為了有人搜尋時能找到這裡,把他們的描述也貼出來:
求高手賜教,本人正在學習基於C#的directx開發,在攝像機旋轉遇到問題了,無法繞X軸旋轉360度。附部分程式碼
(http://bbs.csdn.net/topics/390441832?page=1)
最近在做一個專案是使用C#模擬一些簡單的3D,在網路上早到了一些C#的程式碼,過程還好,但是唯一頭痛的問題就是攝像機旋轉到垂直方向後就不能繼續旋轉,結構翻轉到了原始位置,程式碼如下,希望大家幫我修改一下,我想是做個判斷就可以了,但是我對三維空間不是很清楚。希望大家幫個忙哦。
http://bbs.csdn.net/topics/390441832?page=1
但沒找到解決方案,只能自己研究下看看。
花了半天多總算解決了:在每次轉過上下90度時把upVector取反就行了。
現在想想前面的第二位既然是專案,應該最終也解決了。
相關程式碼:
private void RotateXAxis(float angleSpeed)
{
Rotate(XAxis, angleSpeed);//旋轉
Vector3 lastXAixs = XAxis;//儲存上次的x軸
SetDeviceView();//這裡面XAxis的值發生了變化
Vector3 newXAixs = XAxis;//現在的x軸值
double flag = Vector3.Dot(lastXAixs, newXAixs);
if (flag < 0)//旋轉變化後x軸是否反方向了
{
//修改向上方向
UpVector.X *= -1;
UpVector.Y *= -1;
UpVector.Z *= -1;
}
}
private void RotateYAxis(float angleSpeed)
{
Rotate(ZAxis, angleSpeed);
SetDeviceView();
}
private void Rotate(Vector3 xAxis, float angle)
{
//Console.Write("xAxis:({0},{1},{2})\n", xAxis.X, xAxis.Y, xAxis.Z);
//WriteInfo();
Vector4 tempV4 = Rotate(R, xAxis, angle);
//Console.Write("tempV4:({0},{1},{2},{3}) {4}\n", tempV4.X, tempV4.Y, tempV4.Z, tempV4.W, tempV4.Length());
Postion.X = Target.X + tempV4.X;
Postion.Y = Target.Y + tempV4.Y;
Postion.Z = Target.Z + tempV4.Z;
//Console.Write("Postion:({0},{1},{2})\n", Postion.X, Postion.Y, Postion.Z);
}
以下是研究過程的記錄,比較亂,僅做記錄。
計算檢視矩陣時,z軸是從position指向target的向量zAxis(R),給一個upVector,將upVector和zAxis相乘,得到一個xAxis。根據叉積方向和兩向量垂直,實際上,最後的yAxis應該是和upVector方向平行的。
問題是,特殊情況,如果upVector和zAxis平行呢?
A B 的叉積的大小等於a*b*sin<a,b>,當平行時,方向不定,大小為零了。
這絕對是一種異常情況,或許就是這個導致了90度無法轉過去……
那樣的話,是不是讓upVector不和zAxis平行就行呢?
問題在於讓R繞自身的x軸旋轉的話,必定會轉到y軸,也就是upVector,此時,就會出現異常。
那隻要讓R不繞x軸旋轉就行了吧,比如旋轉的時候給轉動的向量(x軸)加一個(1,1,1)的偏移,不會出現轉不過的問題,但是旋轉軌跡怪怪的,而且無論上下,最終都會在某個角度徘徊。不會形成一個圈。難道是那種無限縮小的圈嗎?
那還是在接近平行時判斷一下,想辦法繞開那個特殊情況?
計算兩向量的夾角,A*B/|A|*|B|=sin<A,B>
可以判斷出來,怎麼繞開呢?多轉一些,直接繞過去?
如果繞著固定的軸 Vector3 XVector = new Vector3(1, 0, 0); 旋轉,則不會轉不過,而是轉過90度後就又重新反向轉回來了。
找到無法繞過去的原因了。
繞著xAxis軸旋轉時,xAxis一般是固定不變的,但繞過某個角度後,xAxis的方向會突然反一下。
然後,相當於又沿反方向繞回來了。不斷地反覆,就停在那了。繞過去之後,要繞的角度就應該變個方向。
感覺和發電機中的電流流向原理有點像,當經過一個角度時,線圈(或其他某個結構)要反一下。
加個標誌能判斷是否反向一次,然後讓標識反一下,就好了。
但是這樣雖然不會停在那裡,還是繞不過90度,就和前面圍繞固定的(1, 0, 0)效果一樣,會重新反向轉回來。
不對,看起來是反轉了,但從數字上看,Position確實是有繞一圈
另外,如果 Postion = new Vector3(10, 10, -50); Target = newVector3(10, 10, 0);。則在繞過去的時候會左右反一下。
而,如果 Postion = new Vector3(10, 10, -50); Target = newVector3(0, 0, 0);。則不會左右顛倒。
說明Target在原點時就不會了。
用茶壺看時發現,實際上Target在原點時也會左右顛倒的,只是原來是中心對稱,看不出來。
可能在那個特殊時刻,要對z軸旋轉一下。
看來是上下左右都顛倒一下,之前只看到左右顛倒也是因為對稱的關係。
對z軸旋轉180度吧。
但攝像機實際是隻是一個向量而已,所謂的沿z軸旋轉式沒有任何效果的。
也就是說,問題可能處在攝像機之外,投影變換嗎?
好像也不是,投影變換沒有相應的引數。
發現攝像機有個引數一直被遺忘,向上方向。上下顛倒的話不就是上下左右都變了嗎?
可以!
不過還有一瞬間的上下左右顛倒,應該是修改upVector的時機不對吧,再改改。
總數解決了,前面的處理都是隻看到問題的一小部分所作的修正,並沒有解決問題核心,雖然也起到一步一步慢慢接近問題核心的作用。
總之就是要修改向上方向(upVector),在每次轉過上下90度時把upVector取反就行了。
實際上從一開始,轉到上下90度的時候,上下左右前後都會顛倒。
之所以為什麼會這樣,可能真的和發電機中的那個現象一樣吧。
不管了,程式能用了。