理解openGL中的矩阵操作

What

openGL中的矩阵是一组提供了接口的二维数组,用来表示图形的变换

空间中点的坐标由一维数组(vector)表示,用矩阵表示点将要进行的变换,相乘后即得到变换后的坐标

从笛卡尔坐标(Cartesian Coordinates)到齐次坐标(Homogeneous Coordinates)

点的坐标可以用一个三维vector表示,进行rotate和scale变换时乘对应的矩阵(3 * 3)即可完成变换。但对translate变换,乘一个3 * 3的矩阵并不能直接完成,因此为了将所有变换的实现都统一到矩阵的乘法,在进行变换时将点的坐标扩展到四维,对应的矩阵也用4 * 4实现,因此openGL中用到的矩阵是4 * 4的

How

openGL中将3D坐标投影到2D屏幕坐标的步骤:

  1. model-view transform
  2. projection transform
  3. apply
  4. viewport transform

以下分别进行说明

Model-View Transform

model指要绘制的物体,view指观察者的位置

这一阶段包括

  1. 物体在场景中的平移、旋转、缩放
  2. 将场景放在观察者面前

因为观察者和物体是相对的,观察者的移动相当于物体往相反方向移动,因此两者可以统一为model-view transform

相关函数

  • glTranslate(x, y, z)
  • glRotate(rad, vx, vy, vz)
  • glScale(x, y, z)
  • gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz)

Projection Transform

在model-view transform中完成了建模后,就需要开始将模型投影到观察者眼中

投影变换(projection transform)分为两种,透视投影(perspective projection)和正交投影(orthographic projection)

Perspective projection

透视投影即近小远大的观察方式

这一步首先要继续对场景进行裁剪(clip),通过上、下、左、右、近平面、远平面将能显示的部分裁剪成平截椎体(frustum)

相关函数

  • gluPerspective(fovy, aspect, zNear, zFar)

Orthographic Projection

正交投影即远近一样大的观察方式

同样进行裁剪

相关函数

  • glOrtho(left, right, bottom, top, near, far)

Apply

openGL根据前面的变换得到的矩阵进行计算

Viewport Transform

Apply结束后就得到了2D的投影视图,我们可以根据需要把它放置到当前窗口(window)的指定位置

相关函数

  • glViewport(x, y, width, height)

Programing

代码参考 projection.c

Perspective projection:

glLookAt()先指定观察者的位置、视线的方向、视线的上方,然后glPerspective()/glOrtho()进行剪切,留下可见的部分

需要注意,__glTranslate()/glRotate()等函数在对物体变换的同时也会对坐标系进行变换__。例如,先glTranslate(1, 0, 0)再glRotate(angle, 0, 1, 0),最终物体不是绕着(0, 0, 0)->(0, 1, 0)的轴旋转,而是绕着平移后的坐标轴的原点(1, 0, 0)->(1, 1, 0)的轴旋转,坐标轴改变后同样会影响之后的gluLookAt()等函数的设置,因此一般最后再进行平移旋转

1
2
3
4
5
6
7
8
9
10
11
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glPerspective(forv, aspect, near, far); / glOrtho(left, right, bottom, top, near, far);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(eye_x, eye_y, eye_z, look_x, look_y, look_z, up_x, up_y, up_z);
glTranslate(x, y, z);
glRotate(angle, x, y, z);
glScale(x, y, z);
draw();

参考资料

《OpenGL编程指南》第5章