线性变换
仿射变换:包括平移、旋转以及比例变换。这种变换能够保持直线建的平行性,并且可逆。
射影变换:包括透视变换等。由于这些变换都是将三维实体投影到二维空间,所以不可逆。
齐次坐标系
OpenGL实际是在四维坐标中定义的,坐标为(x,y,z,w)在三维点空间上显示为(x/w,y/w,z/w),不定义w的话,w默认为1。
故我们用于建模、观察以及投影的所有变换都可由4*4的矩阵直接作用于齐次坐标系中的点和向量得到。
模型-视图变换和投影变换
每个顶点都经过当前模型-视图矩阵和投影矩阵定义的两个变换。一开始这两个矩阵均被设为4*4的单位矩阵。模型-视图矩阵用于对摄影机定位,而投影矩阵指定了投影以及剪裁体,并将顶点映射至一个归一化的坐标系中。
平移
void glTranslate<fd>(type x,type y,type z)
//通过将以参数dx,dy,dz为参数的平移矩阵与当前做右乘来改变当前矩阵,type为GLfloat或GLdouble.
如果我们想所有的顶点沿着z轴负方向平移一个单位,可以这样做
glMatriMode(GL_MODELVIEW); glLoadIdentify(); glTranslatef(0.0,0.0,-1.0);
联级变换
如果没使用glLoadIdentify()则两次平移变换就组合在一起或级联,从而形成一次复合变换。
旋转变换
void glRotate<fd>(type angle,type dx,type dy,type dz)
//形成一个旋转轴为(dx,dy,dz)的旋转矩阵,旋转不动点为原点坐标原点。type为GLfloat或GLdouble。
若要绕着任意固定的点旋转,则可以先将该点平移至原点,然后使用glRotate*()实现所期望的旋转。最后,我们还需要通过一次平移在将该不动点平移回去。
比例变换
void glScale<fd>(type sx,type sy,type sz)
//根据比例因子sx,sy和sz创建一个比例变换矩阵,其不动点位于原点处。type可取GLfloat和GLdouble
一个旋转的立方体:
#include <gl/glut.h> #include <math.h> #include <vector> #include <iostream> using namespace std; int axis=0; float theta[3]; GLfloat vertices[]={ -1.0,-1.0,1.0, -1.0,1.0,1.0, 1.0,1.0,1.0, 1.0,-1.0,1.0, -1.0,-1.0,-1.0, -1.0,1.0,-1.0, 1.0,1.0,-1.0, 1.0,-1.0,-1.0 }; //定义立方体的8个顶点 GLint index[]={ 0,3,2,1, 2,3,7,6, 3,0,4,7, 1,2,6,5, 4,5,6,7, 5,4,0,1 }; //定义每个面所需要那几个顶点 GLfloat colors[]={ 1.0,0.0,0.0, 0.0,1.0,1.0, 1.0,1.0,0.0, 0.0,1.0,0.0, 0.0,0.0,1.0, 1.0,0.0,1.0, 1.0,1.0,0.0, 0.0,1.0,1.0 }; //定义六个面的颜色 void init() { glClearColor(0.0,0.0,0.0,0.0); //指定屏幕背景为黑色 glColor3f(1.0,1.0,1.0); //设置绘制颜色为白色 glShadeModel(GL_FLAT); //设置颜色插值为平面模式 } void display() { glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清理屏幕颜色为我们指定的颜色 glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(3,GL_FLOAT,0,vertices); glColorPointer(3,GL_FLOAT,0,colors); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(theta[0],1.0,0.0,0.0); glRotatef(theta[1],0.0,1.0,0.0); glRotatef(theta[2],0.0,0.0,1.0); glDrawElements(GL_QUADS,24,GL_UNSIGNED_INT,index); glFlush(); //强制以上绘图操作执行 } void reshape(int w,int h) { glMatrixMode(GL_PROJECTION); //设置为投影模式 glLoadIdentity(); glOrtho(-2.0,2.0,-2.0,2.0,-2.0,2.0); glViewport(0,0,(GLsizei)w,(GLsizei)h); } void mouse(int button,int state,int x,int y) { if (button==GLUT_LEFT_BUTTON && state==GLUT_DOWN) { axis=0; } if (button==GLUT_MIDDLE_BUTTON && state==GLUT_DOWN) { axis=1; } if (button==GLUT_RIGHT_BUTTON && state==GLUT_DOWN) { axis=2; } } void SpinIdle() { theta[axis]+=0.1; if(theta[axis] >360.0) theta[axis] -=360.0; glutPostRedisplay(); } int main(int argc,char**argv) { glutInit(&argc,argv); //初始化glut glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); //设置窗口模式为单缓冲和RGB模式 glutInitWindowSize(500,500); //设置窗口大小 init(); glutCreateWindow("test"); //设置窗口标题 glutDisplayFunc(display); //设置绘图回调函数 glutReshapeFunc(reshape); //设置窗口回调函数 glutMouseFunc(mouse); glutIdleFunc(SpinIdle); glutMainLoop(); //开始循环,等待响应 return 0; }
运行并适当点击鼠标改变角度可以得到如下结果:
直接设置矩阵
void glLoadMatrix<fd>(type* m)
//将type类型(可取GLfloat或者GLdouble的数组m加载为当前矩阵)
void glLoadMatrix<fd>(type* m)
//将矩阵m右乘以当前矩阵