繪制四棱錐
四棱錐由5個面構成一個封閉的立體圖,其中4個共頂點的側面是三角形,底面是個四邊形。如果我們要繪制一個3D的四棱錐只需要繪制這5個面即可,繪制的方法和前一篇文章OpenGL_Qt學習筆記之_03(平面圖形的着色和旋轉)的相同。只不過這里的頂點坐標是3維的,所以圖像深度那一維不一定為0。因此我們可以事先計算好四棱錐各個頂點的坐標,這對學過立體幾何的人來說應該是小case了。然后繪制每個面就可以。
注意,在opengl中繪制每個面時,所有面給出的頂點的順序都要按照逆時針或者順時針(我這里采用的是逆時針),這樣才能保證所繪制出來的圖像時正確的。
現在我們在paintGL中開始繪制四棱錐,如果按照NeHe的教程,它只是繪制了個金字塔,並沒有底面,只有4個側面,這里,我采用它的方法,代碼如下:
/*下面開始畫四棱錐*/ glLoadIdentity();//重置當前的模型觀察矩陣 glTranslatef(-0.5, 0.0, -0.5);//將繪制平面移動到屏幕的左半平面和里面 glRotatef(x_rotate, 0.2, 0.2, 0.0); glBegin(GL_TRIANGLES); /*前正面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(-0.3, -0.3, 0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(0.3, -0.3, 0.3); /*右側面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(0.3, -0.3, 0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(0.3, -0.3, -0.3); /*后側面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(0.3, -0.3, -0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(-0.3, -0.3, -0.3); /*左側面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(-0.3, -0.3, -0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(-0.3, -0.3, 0.3); x_rotate_angle1 += 3.0; glEnd();
在繪制完金子塔后,把它沿某一個方向旋轉后如下圖所示:
如果我們在后面加上代碼,把底面補全,畫上一個四邊形,此時加入的代碼如下:
/*底面四邊形的繪制,使四棱錐封閉起來*/ glBegin(GL_QUADS); glColor3f(0.0, 0.0, 1.0);//上頂點紅色 glVertex3f(-0.3, -0.3, 0.3); glColor3f(0.0, 1.0, 0.0);//左下點藍色 glVertex3f(0.3, -0.3, 0.3); glColor3f(0.0, 0.0, 1.0);//右下角綠色 glVertex3f(0.3, -0.3, -0.3); glColor3f(0.0, 1.0, 0.0); glVertex3f(-0.3, -0.3, -0.3); glEnd();
這時候的結果如下:
繪制立方體
繪制立方體的方法和四棱錐的方法類似,只不過這里是由6個正方形構成的封閉體,我們依次繪制出每個面即可,同樣要注意的是繪制每個面時給出點的順序要一致,繪制每個面的順序倒不需要按照什么逆時針或者順時針,什么順序都行。
計算好正方體的8個頂點坐標后就開始寫代碼了,代碼如下:
/*下面開始畫立方體*/ glLoadIdentity(); glTranslated(0.5, 0, 0.5);//將繪制平面移動到屏幕的右半平面和外面 glRotatef(rotate_angle2, -0.2, 0.2, -0.3); glBegin(GL_QUADS); //上頂面 glColor3f(0.0, 1.0, 0.0); glVertex3f(-0.3, 0.3, -0.3); glVertex3f(-0.3, 0.3, 0.3); glVertex3f(0.3, 0.3, 0.3); glVertex3f(0.3, 0.3, -0.3); //下頂面 glColor3f(0.0, 1.0, 0.0); glVertex3f(-0.3, -0.3, -0.3); glVertex3f(-0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, -0.3); //正前面 glColor3f(1.0, 0.0, 0.0); glVertex3f(-0.3, 0.3, 0.3); glVertex3f(-0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, 0.3); glVertex3f(0.3, 0.3, 0.3); //右側面 glColor3f(1.0, 1.0, 0.0); glVertex3f(0.3, 0.3, 0.3); glVertex3f(0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, -0.3); glVertex3f(0.3, 0.3, -0.3); //背后面 glColor3f(0.0, 1.0, 1.0); glVertex3f(-0.3, 0.3, -0.3); glVertex3f(0.3, 0.3, -0.3); glVertex3f(0.3, -0.3, -0.3); glVertex3f(-0.3, -0.3, -0.3); //左側面 glColor3f(1.0, 0.0, 1.0); glVertex3f(-0.3, 0.3, -0.3); glVertex3f(-0.3, -0.3, -0.3); glVertex3f(-0.3, -0.3, 0.3); glVertex3f(-0.3, 0.3, 0.3); rotate_angle2 -= 3; glEnd();
其效果如下:
當兩者放在一起,且經過不同軸的旋轉后圖像如下:
實驗主要部分代碼如下(附錄有工程code下載地址):
#include "glwidget.h" #include "ui_glwidget.h" #include <QtGui> #include <QtCore> #include <QtOpenGL> GLWidget::GLWidget(QGLWidget *parent) : QGLWidget(parent), ui(new Ui::GLWidget) { // setCaption("The Opengl for Qt Framework"); ui->setupUi(this); fullscreen = false; rotate_angle1 = 0.0; rotate_angle2 = 0.0; } //這是對虛函數,這里是重寫該函數 void GLWidget::initializeGL() { setGeometry(300, 150, 640, 480);//設置窗口初始位置和大小 glShadeModel(GL_SMOOTH);//設置陰影平滑模式 glClearColor(0.0, 0.0, 0.0, 0);//改變窗口的背景顏色,不過我這里貌似設置后並沒有什么效果 glClearDepth(1.0);//設置深度緩存 glEnable(GL_DEPTH_TEST);//允許深度測試 glDepthFunc(GL_LEQUAL);//設置深度測試類型 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//進行透視校正 } void GLWidget::paintGL() { //glClear()函數在這里就是對initializeGL()函數中設置的顏色和緩存深度等起作用 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /*下面開始畫四棱錐*/ glLoadIdentity();//重置當前的模型觀察矩陣 glTranslatef(-0.5, 0.0, -0.5);//將繪制平面移動到屏幕的左半平面和里面 glRotatef(rotate_angle1, 0.2, 0.2, 0.0); glBegin(GL_TRIANGLES); /*前正面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(-0.3, -0.3, 0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(0.3, -0.3, 0.3); /*右側面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(0.3, -0.3, 0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(0.3, -0.3, -0.3); /*后側面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(0.3, -0.3, -0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(-0.3, -0.3, -0.3); /*左側面的繪制*/ glColor3f(1.0, 0.0, 0.0);//上頂點紅色 glVertex3f(0.0, 0.3, 0.0); glColor3f(0.0, 0.0, 1.0);//左下點藍色 glVertex3f(-0.3, -0.3, -0.3); glColor3f(0.0, 1.0, 0.0);//右下角綠色 glVertex3f(-0.3, -0.3, 0.3); rotate_angle1 += 3.0; glEnd(); /*底面四邊形的繪制,使四棱錐封閉起來*/ glBegin(GL_QUADS); glColor3f(0.0, 0.0, 1.0);//上頂點紅色 glVertex3f(-0.3, -0.3, 0.3); glColor3f(0.0, 1.0, 0.0);//左下點藍色 glVertex3f(0.3, -0.3, 0.3); glColor3f(0.0, 0.0, 1.0);//右下角綠色 glVertex3f(0.3, -0.3, -0.3); glColor3f(0.0, 1.0, 0.0); glVertex3f(-0.3, -0.3, -0.3); glEnd(); /*下面開始畫立方體*/ glLoadIdentity(); glTranslated(0.5, 0, 0.5);//將繪制平面移動到屏幕的右半平面和外面 glRotatef(rotate_angle2, -0.2, 0.2, -0.3); glBegin(GL_QUADS); //上頂面 glColor3f(0.0, 1.0, 0.0); glVertex3f(-0.3, 0.3, -0.3); glVertex3f(-0.3, 0.3, 0.3); glVertex3f(0.3, 0.3, 0.3); glVertex3f(0.3, 0.3, -0.3); //下頂面 glColor3f(0.0, 1.0, 0.0); glVertex3f(-0.3, -0.3, -0.3); glVertex3f(-0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, -0.3); //正前面 glColor3f(1.0, 0.0, 0.0); glVertex3f(-0.3, 0.3, 0.3); glVertex3f(-0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, 0.3); glVertex3f(0.3, 0.3, 0.3); //右側面 glColor3f(1.0, 1.0, 0.0); glVertex3f(0.3, 0.3, 0.3); glVertex3f(0.3, -0.3, 0.3); glVertex3f(0.3, -0.3, -0.3); glVertex3f(0.3, 0.3, -0.3); //背后面 glColor3f(0.0, 1.0, 1.0); glVertex3f(-0.3, 0.3, -0.3); glVertex3f(0.3, 0.3, -0.3); glVertex3f(0.3, -0.3, -0.3); glVertex3f(-0.3, -0.3, -0.3); //左側面 glColor3f(1.0, 0.0, 1.0); glVertex3f(-0.3, 0.3, -0.3); glVertex3f(-0.3, -0.3, -0.3); glVertex3f(-0.3, -0.3, 0.3); glVertex3f(-0.3, 0.3, 0.3); rotate_angle2 -= 3; glEnd(); } //該程序是設置opengl場景透視圖,程序中至少被執行一次(程序啟動時). void GLWidget::resizeGL(int width, int height) { if(0 == height) height = 1;//防止一條邊為0 glViewport(0, 0, (GLint)width, (GLint)height);//重置當前視口,本身不是重置窗口的,只不過是這里被Qt給封裝好了 glMatrixMode(GL_PROJECTION);//選擇投影矩陣 glLoadIdentity();//重置選擇好的投影矩陣 // gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);//建立透視投影矩陣 glMatrixMode(GL_MODELVIEW);//以下2句和上面出現的解釋一樣 glLoadIdentity(); } void GLWidget::keyPressEvent(QKeyEvent *e) { switch(e->key()) { //F1鍵為全屏和普通屏顯示切換鍵 case Qt::Key_F1: fullscreen = !fullscreen; if(fullscreen) showFullScreen(); else { setGeometry(300, 150, 640, 480); showNormal(); } updateGL(); break; //Ese為退出程序鍵 case Qt::Key_Escape: close(); } } GLWidget::~GLWidget() { delete ui; }
總結:本文在前面文章繪制2D圖像和旋轉的基礎上,增加一維的坐標就可以繪制出3D圖形即旋轉了。在畫3D圖時,必須將OpenGL屏幕想象成一張很大的畫紙,后面還帶着許多透明的層。差不多就是個由大量的點組成的立方體。這些點從左至右、從上至下、從前到后的布滿了這個3D圖的表面。
參考資料:
http://www.owlei.com/DancingWind/
http://www.qiliang.net/old/nehe_qt/
附錄: