OpenGL里面的旋转缩放与平移其实就是坐标系的相关变换,不过由于初学者(像我)很容易被OpenGL里面各种坐标系搞晕而难以理解,现在将我的理解记录如下。PS: 由于学校课程要求,使用的是过时的固定管线。
为了清楚地理解这三种变换,我们只关心两个坐标系:建模坐标系和世界坐标系。眼坐标之类的使用默认值。文末有一段程序代码,绘制了一个正方体,并实现了这三种变换。
世界坐标系(WC):你现在正对绘图窗口,右方是世界坐标系的x轴,上方是y轴,从屏幕指向你的是z轴。
模型坐标系(MC):使用glVertex()
之类的函数定义图元时用的坐标系,初始时MC和WC是重合的。
下文中的图,用蓝色表示MC,黑色表示WC
旋转时世界坐标系不变,但是建模坐标系会旋转,所以旋转之后MC和WC是不重合的。
旋转需要选定一个向量以及一个角度,相应的函数为glRotatef()
,注意:这个向量指的是建模坐标系下的一个向量,建模坐标系绕着这个向量旋转。
例如:绕y轴旋转30度,向量为(0, 1, 0),角度为30。角度可正可负,opengl里面的是右手坐标系,所以旋转的正方向就是右手大拇指朝向旋转向量所指方向时,四个手指所指方向。下图为绕(0, 1, 0)旋转30度。
这样其实也不难理解,但是当时被自己写的代码坑到了。
文末的代码中,旋转是在一对glPushMatrix()
和 glPopMatrix()
中的,所以你在屏幕上看到的图形是由最开始的MC旋转得到的,而不是由上次旋转后的MC再次旋转得到的。
在我写的程序中显示的图形是由绕x, y, z轴依次旋转而得到的(当然也可以不绕着坐标轴),按下xyz键会改变绕xyz轴旋转的角度r_x, r_y, r_z,然后依次地绕xyz轴旋转而得到最终显示的图形。
现在有一种情形,绕xyz轴旋转的角度为r_x, r_y, r_z且均不为0,图形已经绘制好了,我现在按着y键,使r_y一直增加,我看到的是图形绕着y轴转动了一定角度吗?
不是。由于是依次绕着xyz轴旋转,r_y增加,所以受影响的只是基于最开始的MC绘制时,绕y轴旋转的角度,但是绕y轴之后还有绕z轴转动啊,最终的到的图形与之前旋转了r_x,r_y,r_z时相比,并不是绕y轴旋转。如果想直观观察到,可以编译运行下面的代码,先胡乱按xyz,然后按着y键不放。
平移时MC相对与WC的位置不变,但是MC里面所有的坐标都将在原来的基础上加上偏移量(dx, dy, dz)。使用的函数为glTranslatef()
,平移相对来说比较容易理解,不详细叙述了。
先说说现实中的缩放,如果你想要一个物体变小,有两个方法:
同理,OpenGL里面你如果想要对一个图形进行缩放,可以用glOrtho()
之类的函数调整投影区域,也可以使用glScalef()
等将图形的各个坐标缩小或放大。
#include
#include
#include
#include
#include
#include
/*---------------全局变量---------------------*/
//--------------------------------------------
const double PI = 3.14159;
//旋转角度
GLfloat r_xyz[3] = {0.0f};
//平移偏移量
GLfloat dx=0, dy=0, dz=0;
//缩放比例
double radio = 1.0d;
int cx = 200, cy = 200; //中心坐标窗口
//窗口大小
int w = 400;
int h = 400;
//标记是否着色,默认着色
int mode = 1;
/*------------------函数声明--------------------*/
//---------------------------------------------
void onDisplay();
void onReshape(int , int);
void Drawing();
void myRotate();
void rgb(int r, int g, int b);
void specialKeys(int key, int x, int y);
void rgb(int r, int g, int b)
{
glColor3f(r/255.0f, g/255.0f, b/255.0f);
}
void onDisplay()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
Drawing();
glutSwapBuffers();
}
void onReshape(int width, int height)
{
GLfloat aspect = (GLfloat) w/(GLfloat)h;
GLfloat nRange = 400;
w = width;
h = height;
int min = w > h ? h : w;
glViewport(0, 0, min, min);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-nRange, nRange, -nRange , nRange, -nRange, nRange);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glutSwapBuffers();
glutPostRedisplay();
}
void myRotate()
{
int vec[3][3] = {
{1, 0, 0},
{0, 1, 0},
{0, 0, 1}
};
for (int i = 0 ; i <= 2 ; i ++)
{
glRotatef(r_xyz[i], vec[i][0], vec[i][1], vec[i][2]);
}
}
void Drawing()
{
glPushMatrix();
glLoadIdentity();
//旋转
myRotate();
glScalef(radio, radio, radio);
glTranslatef(dx, dy, dz);
if (mode == 2)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glLineWidth(3);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
//顶点向量
//下面表示一个正方体
int A[] = {-100, -100, 100};
int a[] = {-100, -100, -100};
int B[] = {100, -100, 100};
int b[] = {100, -100, -100};
int C[] = {100, 100, 100};
int c[] = {100, 100, -100};
int D[] = {-100, 100, 100};
int d[] = {-100, 100, -100};
glBegin(GL_QUADS);
//front tomato
rgb(234, 67, 53);
glVertex3iv(A);
glVertex3iv(B);
glVertex3iv(C);
glVertex3iv(D);
//fruit salad
rgb(52, 168, 83);
glVertex3iv(D);
glVertex3iv(C);
glVertex3iv(c);
glVertex3iv(d);
//gray
rgb(120, 120, 120);
glVertex3iv(d);
glVertex3iv(c);
glVertex3iv(b);
glVertex3iv(a);
//white
rgb(255, 255, 255);
glVertex3iv(a);
glVertex3iv(b);
glVertex3iv(B);
glVertex3iv(A);
//yellow
rgb(251, 188, 5);
glVertex3iv(a);
glVertex3iv(A);
glVertex3iv(D);
glVertex3iv(d);
//blue
rgb(66, 132, 243);
glVertex3iv(B);
glVertex3iv(C);
glVertex3iv(c);
glVertex3iv(b);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void keyFunc(unsigned char key, int x, int y)
{
switch(key)
{
case 'z': r_xyz[2] += 2; break;
case 'Z': r_xyz[2] -= 2; break;
case 'y': r_xyz[1] += 2; break;
case 'Y': r_xyz[1] -= 2; break;
case 'x': r_xyz[0] += 2; break;
case 'X': r_xyz[0] -= 2; break;
case '-': if (radio > 0)radio -= 0.01; break;
case '+': radio += 0.01; break;
case 'q': exit(0);
case '1': mode = 1;break;
case '2': mode = 2; break;
}
glutPostRedisplay();
}
void specialKeys(int key, int x, int y)
{
switch(key)
{
case GLUT_KEY_UP: dy += 4;break;
case GLUT_KEY_DOWN: dy -= 4; break;
case GLUT_KEY_LEFT: dx -= 4; break;
case GLUT_KEY_RIGHT: dx += 4; break;
}
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(10, 10);
glutInitWindowSize(400, 400);
glutCreateWindow("this is not what I wanted");
glutDisplayFunc(onDisplay);
glutReshapeFunc(onReshape);
glutKeyboardFunc(keyFunc);
glutSpecialFunc(specialKeys);
glutMainLoop();
}