GL_PROJECTION and GL_MODELVIEW

本文深入探讨了OpenGL中关于相机和变换的概念误区,解释了如何使用投影矩阵和模型视图矩阵来实现3D图形的变换,并通过实例展示了如何正确设置这些矩阵以模拟相机运动。
 

Cameras are not the right way to think about OpenGL

Sigh, the tales of an old programmer. I'm teaching myself 3d graphics. My bag of 2d tricks has been used up, 3d has been around for almost 10 years in the mainstream, and it's time to move on.

To learn, I set out to make a little 3d representation of the mandelbrot set. I thought I could just whip that baby out in no time. But, as it turned out, I can't. I had to overcome some basic hurdles with Opengl.

A few things went through my head.

A lot of OpenGL tutorials talk about cameras, and yet, OpenGL doesn't have cameras. Instead, you get a bevy of matrixes and glMatrixModes. Computer science is full of horrific analogies, and I really think that "camera" metaphor people use to describe OpenGL is one of them. I ask myself, in my best Battlestar Galactica parlance, "what the frak going on!!!!"

As I was smashing my keyboard with my fist, causing my shepherd to hide under the bed, two things dawned on me. First is, thank God Logitech keyboards are indestructible. Second, all these matrixes do is move pixels around. There's nothing magic going on. The camera is a lie.

OpenGL is about a set of vertices, or points, in a 3d space. You draw them into 3d coordinates using any of various primitives, such as TRIANGLE_FAN, TRIANGLE_LIST, etc. OpenGL marches through each of these points and "transforms" them into a set of coordinates that look three-dish on screen.

Thus, when you specify various transformations, you are telling OpenGL how to move your points around. With that perspective, you can understand GL_PROJECTION and GL_MODELVIEW this way:

GL_PROJECTION is a matrix transformation that is applied to every point that comes after it, always. GL_MODELVIEW is a matrix transformation that is applied to every point in a particular model. There's a hierarchy of transformations, with GL_PROJECTION at the top, and a set of GL_MODEL branches. You set the matrix transformation with glMatrixMode. By default, the matrix mode is GL_MODELVIEW, which assumes everything you draw will be in one 3d space.

Let's talk about GL_PROJECTION first.



 glMatrixMode(GL_PROJECTION);

 glLoadIdentity();

 glOrtho(-1, 1, -1, 1, -1.0, 1.0);


It's all about matrix math. I basically use the glOrtho to apply some perspective to what I'm doing. Everything in 3d graphics is about matrix math.

Once you set the matrix mode, each subsequent operation is applied to that particular matrix mode and below it. They almost should have called it matrix levels and not matrix modes. I load identity first to give it a place to start because most of the gl matrix commands work by multiplying themselves against whatever the matrix was for that mode before. For example:



 glMatrixMode(GL_PROJECTION);

 glLoadIdentity();

 glOrtho(-1, 1, -1, 1, -1.0, 1.0);

 glTranslate( 100, 100, 100 );

 glRotateF( 45, 1, 0, 0 );


Really means: GL_PROJECTION_MATRIX = IDENTITY * ORTHOGRAPHIC_MATRIX * TRANSLATION_MATRIX * ROTATION_MATRIX.

The order of these calls seems extremely important.

So what's MODEL_VIEW all about? GL_PROJECTION transformations are always applied, so, we can use it to define a camera, set the perspective, among other things. What model view lets us to do is set up different measuring systems for vertices of different things. I select model view by calling



 glMatrixMode(GL_MODELVIEW);


Then I can apply stuff to the model:



 glLoadIdentity();

 glTranslate( modelx, modely, modelz );


To move it in space somewhere, for example.

GL_MODELVIEW is about having different objects being pushed into a "world space". The biggest reason is that I can draw each object using coordinates based around 0, merely specifying the translations or rotations or scaling based on how I want my model to go.

To really appreciate this, let's consider a simple example. I want to draw a world that has two things in it, a car and a battleship. To do this, I might:



 glMatrixMode(GL_PROJECTION);

 glLoadIdentity();

 glOrtho(-1, 1, -1, 1, -1.0, 1.0);

 glTranslate( camerax, cameray, cameraz );



 glMatrixMode(GL_MODELVIEW);

 glLoadIdentity();

 glTranslate( carx, cary, carz );

 // draw car here, by specifying various gl vertices



 glLoadIdentity();

 glTranslate( battleshipx, battleshipy, battleshipz );

 // draw battleship here, by specifying various gl vertices



In that example, each car vertice I specify is being altered as follows:

car_vertice_matrix = projection_ortho_matrix * projection_translation * car_translation_matrix

and, for the battleship

battleship_vertice_matrix = projection_ortho_matrix * projection_translation * battleship_translation_matrix

So, to actually draw the stuff, OpenGl takes the projection matrix multiplied by the current model matrix and uses that to translate each specific vertice within that model. Wow, 3d graphics is not hard. Hell I should go write my own OpenGL. You know, if there wasn't the issue of hardware accelaration, it would be tempting. Well, I still got a ways to go.

I'll have my little multithreaded 3d demo up shortly. There's actually a bug in my thread pool that I will be posting a fix for as well.

I remember reading somewhere in a 3d graphics book that the glProjection matrix should not be used to fake a camera. I think that was an evil thing to remember. But, wow, it just seems like it would be such a good spot to do it. What I have to try is something like this:



 glMatrixMode(GL_PROJECTION);

 glLoadIdentity();

 glOrtho(-1, 1, -1, 1, -1.0, 1.0);

 glTranslate( camerax, cameray, cameraz );

 glRotateF( cameraanglex, 1, 0, 0 );


In that case, the glTranslate would be where the camera is, and glRotateF, where it is facing.

Talking about cameras

Now, I know I said that the camera is a lie. It is, because its pretty obvious that we're really moving the entire world to get our picture, and not some "camera".

原文地址:http://www.storkyak.com/2005/11/opengl-glprojection-and-glmodelview.html

#include <GL/glut.h> #include <stdlib.h> #include <stdio.h> #include <SOIL.h> // 纹理ID数组 GLuint textures[3]; // 光照设置 void initLighting() { // 启用光照 glEnable(GL_LIGHTING); // 光源1 (点光源) GLfloat light0_ambient[] = {0.2, 0.2, 0.2, 1.0}; GLfloat light0_diffuse[] = {1.0, 1.0, 1.0, 1.0}; GLfloat light0_specular[] = {1.0, 1.0, 1.0, 1.0}; GLfloat light0_position[] = {1.0, 1.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glEnable(GL_LIGHT0); // 光源2 (平行光) GLfloat light1_ambient[] = {0.1, 0.1, 0.1, 1.0}; GLfloat light1_diffuse[] = {0.8, 0.8, 0.8, 1.0}; GLfloat light1_specular[] = {0.5, 0.5, 0.5, 1.0}; GLfloat light1_position[] = {-1.0, -1.0, -1.0, 0.0}; // w=0表示平行光 glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glEnable(GL_LIGHT1); } // 加载纹理 void loadTextures() { glGenTextures(3, textures); // 纹理1 glBindTexture(GL_TEXTURE_2D, textures[0]); int width, height; unsigned char* image = SOIL_load_image("texture1.jpg", &width, &height, 0, SOIL_LOAD_RGB); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); SOIL_free_image_data(image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理2 glBindTexture(GL_TEXTURE_2D, textures[1]); image = SOIL_load_image("texture2.jpg", &width, &height, 0, SOIL_LOAD_RGB); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); SOIL_free_image_data(image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理3 (用于另一个物体) glBindTexture(GL_TEXTURE_2D, textures[2]); image = SOIL_load_image("texture3.jpg", &width, &height, 0, SOIL_LOAD_RGB); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); SOIL_free_image_data(image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } // 绘制立方体 void drawCube() { glBegin(GL_QUADS); // 前面 glNormal3f(0.0, 0.0, 1.0); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); // 后面 glNormal3f(0.0, 0.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, -1.0); // 上面 glNormal3f(0.0, 1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0); // 下面 glNormal3f(0.0, -1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, -1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); // 右面 glNormal3f(1.0, 0.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0); // 左面 glNormal3f(-1.0, 0.0, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); glEnd(); } // 绘制球体 void drawSphere() { glutSolidSphere(1.0, 50, 50); } // 显示函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 设置材质1 (用于立方体) GLfloat mat_ambient1[] = {0.7, 0.7, 0.7, 1.0}; GLfloat mat_diffuse1[] = {0.8, 0.8, 0.8, 1.0}; GLfloat mat_specular1[] = {1.0, 1.0, 1.0, 1.0}; GLfloat mat_shininess1[] = {50.0}; glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient1); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular1); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess1); // 绘制带纹理的立方体 (使用两种纹理) glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // 前面和后面使用纹理1 glBindTexture(GL_TEXTURE_2D, textures[0]); glPushMatrix(); glTranslatef(-2.0, 0.0, 0.0); glRotatef(30, 1.0, 1.0, 0.0); drawCube(); glPopMatrix(); // 设置材质2 (用于球体) GLfloat mat_ambient2[] = {0.2, 0.2, 0.5, 1.0}; GLfloat mat_diffuse2[] = {0.3, 0.3, 0.8, 1.0}; GLfloat mat_specular2[] = {0.8, 0.8, 0.8, 1.0}; GLfloat mat_shininess2[] = {30.0}; glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient2); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse2); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular2); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess2); // 绘制带纹理的球体 (使用不同纹理) glBindTexture(GL_TEXTURE_2D, textures[2]); glPushMatrix(); glTranslatef(2.0, 0.0, 0.0); drawSphere(); glPopMatrix(); glDisable(GL_TEXTURE_2D); glutSwapBuffers(); } // 键盘回调函数 void keyboard(unsigned char key, int x, int y) { switch (key) { case '1': glShadeModel(GL_FLAT); break; case '2': glShadeModel(GL_SMOOTH); break; case 27: // ESC键 exit(0); break; } glutPostRedisplay(); } // 窗口大小调整 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // 初始化函数 void init() { glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); initLighting(); loadTextures(); // 默认使用平滑着色 glShadeModel(GL_SMOOTH); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutInitWindowPosition(100, 100); glutCreateWindow("真实感图形生成实验"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }改成python代码要求实现一模一样的图
06-11
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值