在黑暗中举起探索的火炬的网志
在喧闹、混杂的生活中
你应该与你的心灵和平相处
尽管这世上有很多假冒和欺骗
有很多单调乏味的工作
和众多破灭的梦幻
它仍然是一个美好的世界
记住:你应该努力去追求幸福。
是的,记住:你应该努力去追求幸福。
每个早晨灿烂的太阳升起的时候,
每个人都应
-
2004-04-22
[原创] 用点积和叉乘解决空间中固定朝向物体的摆放问题 - [OpenGL开发专辑]
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
大家都知道可以用下面的方法将一个物体摆放在opengl三维场景中的某个位置: glPushMatrix(); glScalef(fZoomValue); //缩放 glTranslatef(xTrans,yTrans,zTrans); //平移 glRotatef(xRot,1,0,0 ); //绕x轴旋转 glRotatef(yRot,0,1,0 ); //绕y轴旋转 glRotatef(zRot,0,0,1 ); //绕z轴旋转 DrawSomeObj(**); //画物体 glPopMatrix(); 如果是简单场景,即我们知道其绕三轴的夹角,那此方法是可行的,但是,如果要求在点(1,2,3)和点(7,8,9)之间绘制一个柱面,其轴线就是两点的连线,请问上面的办法如何处理。 有人说进行三角函数,计算出夹角,在结果上可以,但函数复杂,速度慢,还有一个变换一次后角度改变的问题,很容易就出错。建议大家不要用此方法。我推荐用点积和叉乘来一次性解决问题。 首先看glRotatef的定义: glRotatef(Angle,vx,vy,vz); Angle是一个标量,是变换前后的角度差; vx,vy,vz组成一个矢量,表示旋转的轴; 只要可以计算出他们的值,就可以一次性将物体朝向旋转到位,而不需要进行开始时的三次旋转。 其次我们来看看点积和叉乘的数学定义: 点积: (x1 , y1 , z1 ) .( x2 , y2 , z2 ) = x1x2 + y1y2 + z1z2 叉乘: ( x1 , y1 , z1 ) X ( x2 , y2 , z2 ) =( y1z2 - z1y2 , z1x2 - x1z2 , x1y2 - y1x2 ) 点积可以来计算两矢量的夹角,公式如下: cos (V ^ W) =V.W / | V | | W | 叉乘可以计算两矢量的垂直矢量,叉乘后的新矢量就是垂直于前两矢量的矢量. 这样,Angle,vx,vy,vz就都可以计算出来了,下面请看一个例子: 我要在x1,y1,z1--开始点,x2,y2,z2--结束点之间画一个柱面,画住的原始函数如下: void DrawPrism(float SideLen,int SideNum,float Height){ if(SideNum<3) return; if(SideLen<0.000001 || Height<0.000001) return; int i=0; glPushMatrix(); for(i=0;i<SideNum;i++) { glBegin(GL_TRIANGLES); glVertex3f(0,0,Height); glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),Height); glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),Height); glEnd(); } for(i=0;i<SideNum;i++) { glBegin(GL_TRIANGLES); glVertex3f(0,0,0); glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),0); glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),0); glEnd(); } for(i=0;i<SideNum;i++) { glBegin(GL_QUADS); glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),0); glVertex3f(SideLen*cos((i+1)*2*PI/SideNum),SideLen*sin((i+1)*2*PI/SideNum),Height); glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),Height); glVertex3f(SideLen*cos(i*2*PI/SideNum),SideLen*sin(i*2*PI/SideNum),0); glEnd(); } glPopMatrix(); } 其默认是以(0,0,0)为起点,轴向为z正向,(0,0,Height)为终点,要把它"搬"到Point1(x1,y1,z1),Point2(x2,y2,z2)之间,用点积和叉乘就容易办到了,代码如下: double a,b,c,d,a1,b1,c1,a2,b2,c2,d1,d2;//定义矢量 double alfa;//定义旋转角度 //这三步求得Point1和Point2的矢量1 a1 = x2 - x1;// b1 = y2 - y1;// c1 = z2 - z1; //这是旋转前轴向代表的矢量2 a2 = 0; b2 = 0; c2 = 1; d1 = sqrt(a1*a1 + b1*b1 + c1*c1);//求矢量1的长度 d2 = sqrt(a2*a2 + b2*b2 + c2*c2);//求矢量2的长度,其实就是1,这里为了说明没有简化 d = d1;//获得矢量1的长度 alfa = (GLdouble)( acos( abs(a1*a2 + b1*b2 + c1*c2)/d1/d2 ) * 180/PI );//由点积计算旋转角度 //由叉乘计算旋转轴 a = b1*c2 - b2*c1; b = a2*c1 - a1*c2; c = a1*b2 - a2*b1; alfa = -alfa;//修正角度(也可以修改下面的glrotatef函数) if(c1<0) { alfa = 180 - alfa; } glPushMatrix(); //glScalef(fZoomValue); //这里不用考虑缩放,所以不用 glTranslatef(x1,y1,z1); //平移 glRotatef(alfa,a,b,c ); //旋转 DrawPrism(SideLen,4,d); //画柱,函数在上面 glPopMatrix(); 自此函数完成,一般把上述过程优化后写入一个函数里,以方便使用. 如果是已知3轴旋转角,如何简化成一个glRotatef函数呢,用同样的办法就可以办到,复杂的一步是要通过这3轴旋转角计算出新矢量,这个计算过程在许多图形学的书中都有,公式是: 绕X轴旋转角q的矩阵 | 1 0 0 0 | | 0 cos(q) sin(q) 0 | | 0 -sin(q) cos(q) 0 | | 0 0 0 1 | 绕Y轴旋转角q的矩阵: | cos(q) 0 -sin(q) 0 | | 0 1 0 0 | | sin(q) 0 cos(q) 0 | | 0 0 0 1 | 绕Z轴旋转角q的矩阵: | cos(q) sin(q) 0 0 | |-sin(q) cos(q) 0 0 | | 0 0 1 0 | | 0 0 0 1 | 计算即可. 此文属本人原创,转载请著名作者和出处.
http://junglesong.yourblog.org/logs/156454.html
随机文章:
[轉載] 被我们丢失的历史 作者: 侯虹斌 2004-06-18[原创] 本人设计的第一人称三维角色类,请高手指点。 2004-04-22[转载] 漫谈程序员与编程(上) 作者:侯捷 2004-03-25对QPixmap对象进行缩放(整理) 2004-01-26更新记录(2004-1-26) 2004-01-26
收藏到:Del.icio.us





