iCAx开思工具箱
标题:
一篇好文章!
[打印本页]
作者:
XUSIR98
时间:
2008-4-9 22:46
标题:
一篇好文章!
看到一篇好文章,和大家一起分享!
和uf5945函数有点关系!就是变换里的函数!
大家都知道可以用下面的方法将一个物体摆放在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 |
计算即可.
作者:
张少杰
时间:
2010-11-21 15:48
谢谢楼主分享啊
欢迎光临 iCAx开思工具箱 (https://t.icax.org/)
Powered by Discuz! X3.3