本節是OpenGL學習的第四個課時,下面介紹OpenGL點的相關知識:
(1)點的概念:
數學上的點,只有位置,沒有大小。但在計算機中,無論計算精度如何提高,始終不能表示一個無窮小的點。一般情況下,OpenGL中的點將被畫成單個的像素,雖然它可能足夠小,但並不會是無窮小。同一像素上,OpenGL可以繪制許多坐標只有稍微不同的點,但該像素的具體顏色將取決於OpenGL的實現。
點是OpenGL中繪制一切的基礎。
(2)如何繪制點:
1)OpenGL為點的繪制提供了一系列函數:
glVertex2d glVertex2f glVertex3f glVertex3fv //數字表示參數的個數,字母表示參數的類型: s:表示16位整數(OpenGL中將這個類型定義為GLshort), i:表示32位整數(OpenGL中將這個類型定義為GLint和GLsizei), f:表示32位浮點數(OpenGL中將這個類型定義為GLfloat和GLclampf), d:表示64位浮點數(OpenGL中將這個類型定義為GLdouble和GLclampd)。 v:表示傳遞的幾個參數將使用指針的方式。
//OpenGL的很多函數都是采用這樣的形式,一個相同的前綴再加上參數說明標記。同時用glVertex*來表示這一系列函數。
上面這些函數雖然函數名和參數都不相同,但功能都類似:
//下面5段代碼是等效的: (一)glVertex2i(1, 3); (二)glVertex2f(1.0f, 3.0f); (三)glVertex3f(1.0f, 3.0f, 0.0f); (四)glVertex4f(1.0f, 3.0f, 0.0f, 1.0f); (五)GLfloat VertexArr3[] = {1.0f, 3.0f, 0.0f};glVertex3fv(VertexArr3);
2)繪制不同大小的點:
點的大小默認為1個像素,也可以用glPointSize()函數改變:
#include <GL/glut.h> #pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void paint(void) { glClear(GL_COLOR_BUFFER_BIT); glClearColor(1,1,1,1); glViewport(0, 0, 400, 100); glViewport(0, 0, 100, 100); glPointSize(10.0f); glColor3f(1,0,0); glBegin(GL_POINTS); glVertex2f(0.0f,0.0f); glEnd(); glViewport(100, 0, 100, 100); glPointSize(20.0f); glColor3f(0, 1, 0); glBegin(GL_POINTS); glVertex2f(0.0f, 0.0f); glEnd(); glViewport(200, 0, 100, 100); glPointSize(30.0f); glColor3f(0, 0, 1); glBegin(GL_POINTS); glVertex2f(0.0f, 0.0f); glEnd(); glViewport(300, 0, 100, 100); glPointSize(40.0f); glColor3f(1, 1, 0); glBegin(GL_POINTS); glVertex2f(0.0f, 0.0f); glEnd(); glFlush(); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowSize(400,100); glutInitWindowPosition(500,300); glutCreateWindow("繪制不同大小的點"); glutDisplayFunc(paint); glutMainLoop(); return 0; }

需要注意的點:
1:給所畫圖形指定顏色,用glColor3b()不能達到效果,要用glColor3f();
2.glVertex(float x,float y)中x,y為百分比。范圍均為(-1,1)表示當前區域位置。(0,0)表示中點,(1,1)表示右上角。
3.glViewport()函數的后兩個參數表示區域的寬度和高度,不代表坐標值。
4.在畫圖完畢不要忘了glFlush();
(3)3D空間中點的動態繪制及動態查看:
#include <GL/glut.h> #include <math.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") #define GL_PI 3.1416f // 定義旋轉角度,在按鍵控制中使用 static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; // 設置渲染狀態 void SetupRC() { glClearColor(0.4f, 0.4f, 0.4f, 1.0f); glColor3f(0.0f, 1.0f, 1.0f); } // 繪制場景(顯示回調函數) void RenderScene() { //存儲坐標和角度 GLfloat x, y, z, angle; glClear(GL_COLOR_BUFFER_BIT); // 保存矩陣狀態並旋轉 glPushMatrix(); glRotatef(xRot, 1.0f, 0.0f, 0.0f);//繞x軸旋轉 glRotatef(yRot, 0.0f, 1.0f, 0.0f);//繞y軸旋轉 // 開始繪制頂點圖元 glBegin(GL_POINTS); // 設置Z軸的起始位置 z = -50.0f; for (angle = 0.0f; angle <= (2.0f * GL_PI * 3.0f); angle += 0.1f) { //計算X、Y坐標 x = 50.0f * sin(angle);//相當於在xy平面畫正弦圖像,並且在z軸上有縱深變化。 y = 50.0f * cos(angle); //指定頂點位置 glVertex3f(x, y, z); //對Z坐標值稍作修改,在下一個頂點使用 z += 0.5f; } glEnd(); // 恢復矩陣狀態 glPopMatrix(); // 刷新繪圖命令,此時所有未執行的OpenGL命令被執行 glutSwapBuffers(); } // 當窗口大小改變時由GLUT函數庫調用 void ChangeSize(GLsizei w, GLsizei h) { // 范圍 GLfloat nRange = 100.0f; // 縱橫比 GLfloat aspectRatio; // 防止被0所除 if (0 == h){ h = 1; } // 根據窗口大小設置視口 glViewport(0, 0, w, h); // 選擇投影矩陣,並重置坐標系統 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 計算窗口的縱橫比(像素比) aspectRatio = (GLfloat)w / (GLfloat)h; // 定義裁剪區域(根據窗口的縱橫比,並使用正投影) if (w <= h) {//寬 < 高 glOrtho(-nRange, nRange, -nRange / aspectRatio, nRange / aspectRatio, -nRange, nRange); } else {//寬 > 高 glOrtho(-nRange * aspectRatio, nRange *aspectRatio, -nRange, nRange, -nRange, nRange); } // 選擇模型視圖矩陣,並重置坐標系統 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // 按鍵響應函數 void SpecialKeys(int key, int x, int y) { if (key == GLUT_KEY_UP) xRot -= 5.0f; if (key == GLUT_KEY_DOWN) xRot += 5.0f; if (key == GLUT_KEY_LEFT) yRot -= 5.0f; if (key == GLUT_KEY_RIGHT) yRot += 5.0f; if (key> 356.0f) xRot = 0.0f; if (key< -1.0f) xRot = 355.0f; if (key> 356.0f) yRot = 0.0f; if (key< -1.0f) yRot = 355.0f; // 使用新的坐標重新繪制場景 glutPostRedisplay(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(480, 320); glutCreateWindow("3D空間中點的繪制與查看"); // 設置顯示回調函數 glutDisplayFunc(RenderScene); // 設置當窗口的大小發生變化時的回調函數 glutReshapeFunc(ChangeSize); // 設置按鍵響應函數(特殊鍵) glutSpecialFunc(SpecialKeys); // 設置渲染狀態 SetupRC(); // 啟動GLUT框架的運行,一經調用便不再返回,直到程序終止 glutMainLoop(); return 0; }


1)代碼解釋:
1.void glutSpecialFunc(void (*func)(int key, int x, int y));
glutSpecialFunc sets the special keyboard callback for the current window.The special keyboard callback is triggered when keyboard function or directional keys are pressed. The key callback parameter is a GLUT_KEY_* constant for the special key pressed. The x and y callback parameters indicate the mouse in window relative coordinates when the key was pressed. When a new window is created, no special callback is initially registered and special key strokes in the window are ignored. Passing NULL to glutSpecialFunc disables the generation of special callbacks.
During a special callback, glutGetModifiers may be called to determine the state of modifier keys when the keystroke generating the callback occurred. An implementation should do its best to provide ways to generate all the GLUT_KEY_* special keys.
The available GLUT_KEY_* values are:
GLUT_KEY_F1:F1 function key. GLUT_KEY_F2:F2 function key. ...... GLUT_KEY_F12:F12 function key. GLUT_KEY_LEFT:Left directional key. GLUT_KEY_UP:Up directional key. GLUT_KEY_RIGHT:Right directional key. GLUT_KEY_DOWN:Down directional key. GLUT_KEY_PAGE_UP:Page up directional key. GLUT_KEY_PAGE_DOWN:Page down directional key. GLUT_KEY_HOME:Home directional key. GLUT_KEY_END:End directional key. GLUT_KEY_INSERT:Inset directional key. Note that the escape, backspace, and delete keys are generated as an ASCII character.
設置當前窗口的特定鍵的回調函數。x,y:當按下鍵時鼠標的坐標,相對於窗口左上角,以像素為單位。
注意:ESC,回車和delete鍵由ASCII碼產生,即可以用glutKeyboardFunc()處理. 當在鍵盤上敲擊上述按鍵時調用該函數.注意與glutKeyboardFunc()的區別。
2.自定義鍵盤監聽函數(注冊)
一個經常的用法是當按下ESCAPE鍵時退出應用程序。
注意,我們提到過,glutMainLoop函數產生的是一個永無止境的循環。唯一的跳出循環的方法就是調用系統exit函數。這就是我們函數要做的,當按下ESCAPE鍵調用exit函數終止應用程序(同時要記住在源代碼包含頭文件stdlib.h)。下面就是這個函數的代碼:
void processNormalKeys(unsigned char key,int x,int y) { if(key==27) Exit(0); }
我們還可以控制特殊鍵的按鍵消息:
//當在繪圖中動態指定繪圖顏色時: void processSpecialKeys(int key, int x, int y) { switch(key) { case GLUT_KEY_F1 : red = 1.0; green = 0.0; blue = 0.0; break; case GLUT_KEY_F2 : red = 0.0; green = 1.0; blue = 0.0; break; case GLUT_KEY_F3 : red = 0.0; green = 0.0; blue = 1.0; break; } }
3.glPushMatrix()、glPopMatrix()
相當於棧里的入棧和出棧。glPushMatrix其實就是把當前狀態做一個副本放入堆棧之中;glPopMatrix()從棧中取出一個副本,恢復成副本的狀態。
glLoadIdentity(); glTranslatef(1,0,0);//向右移動(1,0,0) glPushMatrix();//保存當前位置 glTranslatef(0,1,0);//現在是(1,1,0)了 glPopMatrix();//這樣,現在又回到(1,0,0)了
4.void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
模型變換函數中的旋轉。angle表示旋轉的角度(注意單位不是弧度),(x,y,z)表示轉軸。
glRotatef(45.0, 0.0, 0.0, 1.0);//表示模型沿着(0,0,1)這個軸旋轉45°。
5.glFlush和glutSwapBuffers[void glFlush(void void);void glutSwapBuffers(void)]
glFlush 是強制馬上輸出命令執行的結果,而不是存儲在緩沖區中,繼續等待其他OpenGL命令。
當執行雙緩沖交換的時候,使用glutSwapBuffers。但是在有 glutSwapBuffers 的情況下,不需要 glFlush 就可以達到同樣的效果,因為我們執行雙緩沖交換的時候,就隱形的執行了一次刷新操作。
6.void glutPostRedisplay(void)與Void glutIdleFunc(void(*f)(void))
glutIdleFunc()空閑回調函數標識了這樣一個函數:當事件隊列中沒有事件需要處理時(即事件隊列為空時),它才有機會得到執行。
這兩個函數經常這樣用:
glutIdleFunc(myidle); void myidle() { glutPostRedisplay(); }
glutPostRedisplay標記當前窗口需要重新繪制。通過glutMainLoop下一次循環時,窗口顯示將被回調以重新顯示窗口的正常面板。多次調用glutPostRedisplay,在下一個顯示回調只產生單一的重新顯示回調。
2)相關知識:
1.雙緩沖技術:
glutSwapBuffers函數是OpenGL中GLUT工具包中用於實現雙緩沖技術的一個重要函數。該函數的功能是交換兩個緩沖區指針。當我們進行復雜的繪圖操作時,畫面便可能有明顯的閃爍。解決這個問題的關鍵在於使繪制的東西同時出現在屏幕上。
雙緩沖技術,是指使用兩個緩沖區: 前台緩沖和后台緩沖。前台緩沖即我們看到的屏幕,后台緩沖則在內存當中,對我們來說是不可見的。每次的所有繪圖操作都在后台緩沖中進行,當繪制完成時, 把繪制的最終結果復制到屏幕上,這樣,我們看到所有GDI元素同時出現在屏幕上,從而解決了頻繁刷新導致的畫面閃爍問題。
在OpenGL中實現雙緩沖技術的一種簡單方法:
1.在調用glutInitDisplayMode函數時, 開啟GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);。這里將我們慣用的GLUT_SINGLE替換為GLUT_DOUBLE,意為要使用雙緩沖而非單緩沖。 2. 調用glutDisplayFunc(display)注冊回調函數時, 在回調函數中所有繪制操作完成后調用glutSwapBuffers()交換兩個緩沖區指針。 3. 調用glutIdleFunc注冊一個空閑時繪制操作函數, 注冊的這個函數再調用display函數。
2.單一的重新顯示回調:
glutPostRedisplay()所執行的功能類似於直接調用顯示回調函數display(),但該函數允許實現在對何時真正需要調用顯示回調函數而作出決策時,變得更加“智能化”。
在GLUT遍歷整個事件循環時,必然會檢索到許多要求窗口重繪的事件。如果每次都去直接調用顯示回調函數,窗口必然會被多次繪制。而使用glutPostRedisplay()之后,就使得在遍歷消息隊列的整個過程中,只對窗口重繪一次。
(4)繪制正弦線上的點:並改變點的大小:
#include <GL/glut.h> #include <math.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") #define GL_PI 3.1416f const GLfloat factor = 0.1f; void SetupRC(void) { glClearColor(0.5f, 0.5f, 0.5f, 0.5f); } void paint(void) { GLfloat x; glColor3f(1.0f, 1.0f, 1.0f); glPointSize(4.0f);//應在glBegin之前 glBegin(GL_POINTS); for (x = -10; x<10; x += 0.1) { glVertex2f(x*factor, sin(x)*factor*5); } glEnd(); glColor3f(1.0f, 0.0f, 0.0f); glBegin(GL_LINES); glVertex2f(-1.0f, 0.0f); glVertex2f(1.0f, 0.0f); glVertex2f(0.0f, -1.0f); glVertex2f(0.0f, 1.0f); glEnd(); glFlush(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//當沒有鍵盤監聽時不需要用雙緩沖模式 glutInitWindowSize(400, 400); glutCreateWindow("正弦點圖"); glutDisplayFunc(paint); SetupRC(); glutMainLoop(); return 0; }

