-
理論基礎
1,OpenGL渲染3D物體到螢幕上的過程其實類似我們平時用照相機拍照的過程,這個步驟大緻如下:一,把照相機固定在三腳架并讓它對準場景(視圖變換)二,把場景中的物體調整擺放好(模型變換)三,選擇照相機的鏡頭,并調整放大倍數(投影變換)四,确定最終照片的大小(視口變換)。其中視圖變換必須要在模型變換之前,其它可以在任何時候。
2,視圖變換:設定錄影機的位置,gluLookAt(錄影機位置,鏡頭瞄準,上方向),其中上方向的了解就類似是一個放好的瓶子,一般指定垂直向上是瓶蓋的方向。預設錄影機位于原點,指向z軸的負方向,朝上向量為(0, 1, 0).
3,模型變換:設定物體的位置和方向,包括:旋轉,移動,縮放,而且要注意組合使用他們的順序,像先平移再旋轉與先旋轉再平移它們的表現是不一樣的。注意,可以不必通過移動錄影機(視圖變換)來觀察物體,而是移動這個物體(模型變換),這兩個方式都能實作同樣地效果,有時使用其中一種變換比使用另一種變換要友善得多。這也是把視圖變換和模型變換都統一為模型視圖矩陣的原因。
4,投影變換:設定視景體,将3維坐标投影為2維螢幕坐标,位于視景體之外的東西将被裁剪掉。它分為兩種投影:正投影和透視投影,其中正投影就是平行投影,不管遠近物體看上去都一樣。而透視投影則是近大遠小的真實效果。為什麼會是這樣?可以這麼了解:投影時會是有個正方體或是平截頭體(就是指視景體),它們的每個切面其實就相當于我們的螢幕,對應正投影的正方體(長方體)當然是不管距離錄影機遠處還是近處,切面都是一樣大,是以物體觀察沒有變化。而對于透視投影的平截頭體,近處的切面小因而物體占據的切面百分比面積要大,即同樣的物體近處填充螢幕的面積要大,是以就形成了近大遠小的真實效果。
正投影:glOrtho(left, right, bottom, top, near, far)或二維圖像的投影gluOrtho2D(left, right, bottom, top)
透視投影:glFrustum(left, right, bottom, top, near, far),或者工具封裝的接口gluPerspective(angle, w/h, near, far)
對應的示意圖如下:
OpenGL--3D世界(視圖變換,模型變換,投影變換,視口變換) OpenGL--3D世界(視圖變換,模型變換,投影變換,視口變換) OpenGL--3D世界(視圖變換,模型變換,投影變換,視口變換) 5,視口變換:其實就是指定最終圖像顯示在螢幕那個區域,一般我們是設定和螢幕一樣大。(glViewport (0, 0, (GLsizei) w, (GLsizei) h))
6,glPushMatrix() 和 glPopMatrix()了解:首先我們要知道,對于矩陣的操作都是對于矩陣棧的棧頂來操作的。目前矩陣即為矩陣棧的棧頂元素,而對目前矩陣進行平移、旋轉等的變換操作也同樣是對棧頂矩陣的修改。是以我們在變換之前調用giPushMatrix()的話,就會把目前狀态壓入第二層,不過此時棧頂的矩陣也與第二層的相同。 當經過一系列的變換後,棧頂矩陣被修改,此時調用glPopMatrix()時,棧頂矩陣被彈出,且又會恢複為原來的狀态。即儲存原來狀态與恢複原來狀态,它與glLoadIdentity ();這個的差別是,這個直接恢複為最初始狀态(0),而它是儲存了目前狀态。示意圖如下:
7,逆變換:我們是經過各種變換将3D空間投影到2D螢幕上的,然而我們滑鼠點選2D螢幕它對應的原來3D空間的哪裡了?這就需要逆變換還原。(gluUnProject())OpenGL--3D世界(視圖變換,模型變換,投影變換,視口變換)
-
代碼示例
1,繪制一個長方體
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#endif
void init(void)
{
glClearColor (, , , );
glShadeModel (GL_FLAT);
}
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT);
glColor3f (, , );
glLoadIdentity ();//矩陣操作之前一般把目前矩陣設定為機關矩陣
//視圖變換
gluLookAt (, , , , , , , , );//設定錄影機位置
//模型變換
glScalef (, , ); //設定物體的位置和方向,包括:旋轉,移動,縮放
glutWireCube (); //繪制立方體
glFlush ();
}
//當視窗初次建立或改變時會被調用
void reshape (int w, int h)
{
//視口變換
glViewport (, , (GLsizei) w, (GLsizei) h);//最終在螢幕上顯示的大小
glMatrixMode (GL_PROJECTION);//設定目前矩陣為投影矩形
glLoadIdentity ();
//投影變換
//glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
/*設定視景體,将3維坐标投影到2維螢幕坐标*/
gluPerspective(, , , );
glMatrixMode (GL_MODELVIEW);//設定目前矩陣為模型視圖矩陣
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (, );
glutInitWindowPosition (, );
glutCreateWindow (argv[]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return ;
}
2,繪制一個裁剪掉了3/4的線框圓(除了視景體的6個裁剪平面外,還可以另外再指定最多可達6個的其他裁剪平面,對視景體施加進一步的限制)
#include <GL/glut.h>
void init(void)
{
glClearColor (, , , );
glShadeModel (GL_FLAT);
}
void display(void)
{
//其它裁剪面方程式Ax + By + Cz + D = 0系數
GLdouble eqn[] = {, , , };
GLdouble eqn2[] = {, , , };
glClear(GL_COLOR_BUFFER_BIT);
glColor3f (, , );
glPushMatrix();
glTranslatef (, , -);//Z軸負方向平移5個機關
/* 裁剪掉y < 0 的部分 */
glClipPlane (GL_CLIP_PLANE0, eqn);
glEnable (GL_CLIP_PLANE0);
/* 裁剪掉x < 0 的部分 */
glClipPlane (GL_CLIP_PLANE1, eqn2);
glEnable (GL_CLIP_PLANE1);
/*逆時針方向繞着從原點到(1.0,0.0,0.0)的直線旋轉90°*/
glRotatef (, , , );
glutWireSphere(, , );//繪制圓
glPopMatrix();
glFlush ();
}
void reshape (int w, int h)
{
glViewport (, , (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective(, (GLfloat) w/(GLfloat) h, , );
glMatrixMode (GL_MODELVIEW);
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case :
exit();
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (, );
glutInitWindowPosition (, );
glutCreateWindow (argv[]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return ;
}
3,模拟行星系統
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#endif
static int day = ;
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); //開啟深度測試
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(, , , );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(, -, , , , , , , );
//太陽
glColor3f(, , );
glutSolidSphere(, , );
//地球
glColor3f(, , );
/*正确地順序是要先平移再旋轉,代碼的寫法要相反*/
glRotatef(day , , , -);
glTranslatef(, , );
glutSolidSphere(, , );
//月亮
glColor3f(, , );
glRotatef(day / * - day , , , -);
glTranslatef(, , );
glutSolidSphere(, , );
glutSwapBuffers();
}
void play()
{
day++;
if (day >= )
day = ;
display();
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize (, );
glutInitWindowPosition (, );
glutCreateWindow (argv[]);
glutDisplayFunc(display);
glutIdleFunc(play);
glutMainLoop();
return ;
}
4,模拟機器人手臂
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#endif
static int shoulder = , elbow = ;
void init(void)
{
glClearColor (, , , );
glShadeModel (GL_FLAT);
}
/*把握一點,opengl是一個狀态機,所有的操作都是在改變狀态而已*/
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT);
glPushMatrix();
/*這裡為什麼不是先畫長方體再執行這裡的移動旋轉,也是由于opengl是一個狀态
機,先畫了,再執行這些變換就已經沒作用了*/
glTranslatef (-, , );
glRotatef ((GLfloat) shoulder, , , );
glTranslatef (, , );
/*push時記住目前的狀态,pop就恢複,如果不這樣,那麼像這裡進行了縮放操作
的狀态就會一直影響以後所有的繪制*/
glPushMatrix();
glScalef (, , );
glutWireCube ();
glPopMatrix();
glTranslatef (, , );
glRotatef ((GLfloat) elbow, , , );
glTranslatef (, , );
glPushMatrix();
glScalef (, , );
glutWireCube ();
glPopMatrix();
glPopMatrix();
glutSwapBuffers();
}
void reshape (int w, int h)
{
glViewport (, , (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective(, (GLfloat) w/(GLfloat) h, , );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef (, , -);
}
void keyboard (unsigned char key, int x, int y)
{
switch (key) {
case 's':
shoulder = (shoulder + ) % ;
glutPostRedisplay();
break;
case 'S':
shoulder = (shoulder - ) % ;
glutPostRedisplay();
break;
case 'e':
elbow = (elbow + ) % ;
glutPostRedisplay();
break;
case 'E':
elbow = (elbow - ) % ;
glutPostRedisplay();
break;
case :
exit();
break;
default:
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (, );
glutInitWindowPosition (, );
glutCreateWindow (argv[]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return ;
}
5,逆變換執行個體
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#endif
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
void reshape(int w, int h)
{
glViewport (, , (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective (, (GLfloat) w/(GLfloat) h, , );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void mouse(int button, int state, int x, int y)
{
GLint viewport[];
GLdouble mvmatrix[], projmatrix[];
GLint realy; //opengl表示的y坐标
GLdouble wx, wy, wz; //逆變換後的3D坐标
switch (button)
{
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN)
{
/*得到對應變換狀态值并儲存到對應變量中*/
glGetIntegerv (GL_VIEWPORT, viewport);
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
/*滑鼠點是視窗左上角是原點而opengl是左下角原點*/
realy = viewport[] - (GLint) y - ;
printf ("Coordinates at cursor are (%4d, %4d)\n", x,
realy);
//逆變換
gluUnProject ((GLdouble) x, (GLdouble) realy, ,
mvmatrix, projmatrix, viewport, &wx,
&wy, &wz);
printf ("World coords at z=0.0 are (%f, %f, %f)\n",
wx, wy, wz);
gluUnProject ((GLdouble) x, (GLdouble) realy, ,
mvmatrix, projmatrix, viewport, &wx,
&wy, &wz);
printf ("World coords at z=1.0 are (%f, %f, %f)\n",
wx, wy, wz);
}
break;
case GLUT_RIGHT_BUTTON:
if (state == GLUT_DOWN)
exit();
break;
default:
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (, );
glutInitWindowPosition (, );
glutCreateWindow (argv[]);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMainLoop();
return ;
}