问题描述:
当绕X轴旋转时,从90度旋转到-90度正常,再继续旋转时无法旋转过去,不能达到360度旋转,求高手赐教。
自己搜索了,有人碰到相同问题:
为了有人搜索时能找到这里,把他们的描述也贴出来:
求高手赐教,本人正在学习基于C#的directx开发,在摄像机旋转遇到问题了,无法绕X轴旋转360度。附部分代码
(http://bbs.youkuaiyun.com/topics/390441832?page=1)
最近在做一个项目是使用C#模拟一些简单的3D,在网络上早到了一些C#的代码,过程还好,但是唯一头痛的问题就是摄像机旋转到垂直方向后就不能继续旋转,结构翻转到了原始位置,代码如下,希望大家帮我修改一下,我想是做个判断就可以了,但是我对三维空间不是很清楚。希望大家帮个忙哦。
http://bbs.youkuaiyun.com/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度的时候,上下左右前后都会颠倒。
之所以为什么会这样,可能真的和发电机中的那个现象一样吧。
不管了,程序能用了。