opengl坐标与矩阵直白剖析

来源:互联网 发布:java b2c商城系统源码 编辑:程序博客网 时间:2024/05/22 06:16

1、opengl的世界坐标系

opengl的默认世界坐标系如图所示,(0,0,0)点位于opengl画布窗口正中心



2、关于glTranslate*()和glRotate*()

含义理解:

glTranslate*(a,b,c)

从当前位置,沿着向量(a,b,c)平移,不是平移到这个点

glRotate*(angle,a,b,c)

沿着从(0,0,0)到(a,b,c)的这个向量,进行旋转angle的角度


实质理解:

实质是进行了一个矩阵乘法,把当前矩阵和一个表示物体移动的矩阵相乘。

有一种很好的理解方法是想象当前画笔所在的Opengl世界坐标系的位置,在未进行任何平移或旋转操作前,每次绘制时画笔都是以原始的世界坐标系作为参考,所绘制的坐标就是所填写的坐标。例如

glBegin(GL_LINES);

    glBegin(GL_LINES);
    glVertex3f(-5.0f,0.0f,0.0f);
    glVertex3f(5.0f,0.0f,0.0f);
    glVertex3f(0.0f,-5.0f,0.0f);
    glVertex3f(0.0f,5.0f,0.0f);
    glVertex3f(0.0f,0.0f,5.0f);
    glVertex3f(0.0f,0.0f,-5.0f);

glEnd();

这个例子就是严格按照上面的三维点,绘制了一个立方体,立方体中心点是世界坐标系中的(0,0,0)。


如果在绘制之前用glTranslate(a,b,c),下面的绘制语句不作任何改变,有以下理解:

(1) 相当于将整个画笔沿(a,b,c)向量平移,然后再进行绘制。

(2) 相当于先在默认世界坐标系画好,再把画出的图形整体向(a,b,c)平移。

(3) 相当于以世界坐标系中的(a,b,c)这个点为新的坐标系的中心点,在新的坐标系下执行刚才的绘制语句。


glTranslate3f(5.0f,5.0f,5.0f);glBegin(GL_LINES);    glBegin(GL_LINES);    glVertex3f(-5.0f,0.0f,0.0f);    glVertex3f(5.0f,0.0f,0.0f);    glVertex3f(0.0f,-5.0f,0.0f);    glVertex3f(0.0f,5.0f,0.0f);    glVertex3f(0.0f,0.0f,5.0f);    glVertex3f(0.0f,0.0f,-5.0f);glEnd();

对于glRotate操作也可以有以上三种理解方法。


3、glTranslate和glRotate 的组合使用

(1) 组合变换,如果以模型为中心,以世界坐标系为参考,模型实际变换的顺序与程序语句相反。

例如

glTranslate3f(3.0f,0.0f,3.0f);glRotate3f(90.0f,0.0f,1.0f,0.0f);

这两个语句的含义是,先把模型绕y轴进行90度旋转,再沿(3.0,0.0,3.0)平移。

而不是先平移到(3,3,3),再沿y轴旋转。

倒着看的话,所有点都是以变换前的世界坐标系(我们默认变换前是世界坐标系)为参考的。


(2)组合变换,以相对坐标系为参考,变换顺序与程序语句顺序相同。

刚才的例子,可以理解为,

坐标系的中心点平移到了(3,3,3),形成了新的坐标系

以(3,3,3)作为新的坐标系的中心点,作一个图形。

再把该图形,在新的坐标系下,沿新的坐标轴的y轴旋转。


对于这前两种理解方式,第一种方式比较好理解。


(3)组合变换使物体沿着不过原点的直线旋转

只用glRotate()时,只能沿着(0,0,0)到(a,b,c)的向量旋转,无法沿不过原点的其他直线旋转。

现在我们想沿着过(1,0,1)这个点,平行于y轴的直线进行旋转90度。

组合使用时,先把y轴平移到(1,0,1),沿着y轴旋转90度,再平移回来。

理解就是,你把旋转轴平移到该平移的位置了,然后旋转完了,物体旋转轴是对了,但是物体本身也发生了不该发生的平移,所以再平移回去。

这一套变化过程是以物体的变化为中心的,而不是坐标轴。因此,程序需要倒过来,写法如下。

glTranslate3f(-1,0,-1); // 平移回去   第三步glRotate3f(0,1,0); //绕新的旋转轴旋转   第二步glTranslate3f(1,0,1); //把旋转轴平移到(1,0,1) 第一步
注意模型的变换顺序是从下往上的,而且我们之前说过,倒着看的话所有点都是以变换前的世界坐标系为参考的。

即:第一步是按照世界坐标系下,平移到了(1,0,1);第二步把物体按照世界坐标系下的(0,1,0)向量去旋转;第三步平移回去,第三步不考虑第二步旋转对坐标系的任何影响。

(4)状态保持

使用了glTranslate和glRotate的单个或组合完成了一次模型变化后,在没有使用glLoadIdentity()之前,之后的绘制都相当于基于之前的变换进行。


3、glLoadIdentity() glPushMatrix() glPopMatrix()

之前我们谈到了glTranslate和glRotate的状态保持。

glLoadIdentity() 用于清除之前的平移旋转状态,使绘图坐标系与opengl默认的世界坐标系重合

glPushMatrix() 用于保存当前的平移或旋转状态(变换矩阵),保存到一个栈的栈顶,下次需要恢复这个状态时,弹出

glPopMatrix()用于将栈顶保存的状态(矩阵)弹出栈顶

例子就是画坐标轴的箭头,先画好三个轴,此时没有平移旋转,坐标轴就是opengl世界坐标系,我们设当前状态为 s1。

画x轴箭头时,要先保存当前状态s1,平移到x轴相应位置画了箭头后,恢复之前的s1状态,再保存s1,平移到y轴画箭头........


//...画了坐标轴glPushMatrix(); //保存状态glTranslatef(5.0f,0.0f,0.0f);glutSolidCone(0.2,0.4,10,10); //画x坐标轴的箭头glPopMatrix();//恢复刚才的状态glPushMatrix();//再保存刚才的状态glTranslatef(0.0f,5.0f,0.0f);glutSolidCone(0.2,0.4,10,10); //画y坐标轴的箭头glPopMatrix();glTranslatef(0.0f,0.0f,5.0f);glutSolidCone(0.2,0.4,10,10); //画z坐标轴的箭头



4、三维视角变换

请记住以下固定用法,在每次画图前在画图函数外或内调用。

<strong>glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(angle, width/height , near , far); glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(eye_x, eye_y, eye_z, lookat_x, lookat_y, lookat_z, up_x, up_y, up_z); </strong>


gluLookAt(eye_x, eye_y, eye_z, lookat_x, lookat_y, lookat_z, up_x, up_y, up_z) 

都是以opengl世界坐标系为准来填写上上述数字,填写时注意把这个想象成你的脑袋,最后这三个不能总设为(0,1,0),恰好在y轴俯瞰时,头顶冲着-z方向,就要设为(0,0,-1)

gluPerspective(angle, width/height , near , far); 

near和far的大小只影响能不能收纳进视野,不影响成像的大小。建议设置为near=0.1,far=1000(尽可能大)

两个语句需要按照上述模板配合使用,分别指出了眼睛位置,眼睛看向哪,和整个的视野范围。


不调用二者设置时,画图往往只能在坐标零点几的范围内变换,因为默认视野离得太近,因此建议每次都设置一下。

设置这个实际上是进行了一个坐标系转换,将世界坐标系转成了相机坐标系。因此glLookAt和绘制语句之间不要加LoadIdentity,这样会使glLookAt作用消失

0 0