本節是OpenGL學習的第七個課時,下面以四邊形為例介紹繪制OpenGL動畫的相關知識:
(1)繪制幾種不同的四邊形:
1)四邊形(GL_QUADS)
OpenGL的GL_QUADS圖元用於繪制四邊形,它根據每四個頂點繪制一個四邊形。
注意:在使用四邊形時必需記住四邊形的四個角必須位於同一個平面中(不存在彎曲的四邊形)。
2)四邊形帶(GL_QUAD_STRIP)
該圖元指定一個連接的四邊形帶。它們都保持相同方向的環繞。
3)通用多邊形GL_POLYGON
我們可以用它繪制任意數量的多邊形。與四邊形一樣,多邊形的所有頂點也必須位於同一平面中。如果想越過這個規則,可以采用一種變通的方法,使用GL_TRIANGLE_FAN代替GL_POLYGON。
4)多邊形的創建規則
1.所有的多邊形都必須是平面的。也就是說,多邊形的所有頂點必須位於同一個平面中。在空間中,多邊形不能扭曲或彎曲。
2.多邊形的邊必須不相交,多邊形必須是凸的。
OpenGL施加這些限制的原因:為了使用一些非常快速的算法對多邊形進行渲染。
5)細分和邊界
盡管OpenGL只能繪制凸多邊形,但我們仍然能夠創建非凸的多邊形,那就是把兩個或多個多邊形排列在一起。
我們使用OpenGL命令glPolygonMode可以把繪圖模式切換到線框模式,這樣就能看到組成較大表面區域的每一個較小的三角形。另外,我們還可以通過glEdgeFlag命令來通知OpenGL哪些線段屬於邊界線(圍繞形狀邊緣的直線),哪些線段不屬於邊界線(形狀內部的直線),這些形狀內部的直線將不可見。這個功能通過使用win API的函數glEdgeFlag()完成。
void glEdgeFlag( GLboolean flag);
Each vertex of a polygon, separate triangle, or separate quadrilateral specified between a glBegin/glEnd pair is marked as the start of either a boundary or nonboundary edge. If the current edge flag is true when the vertex is specified, the vertex is marked as the start of a boundary edge. Otherwise, the vertex is marked as the start of a nonboundary edge. glEdgeFlag sets the edge flag bit to GL_TRUE if flag is GL_TRUE and to GL_FALSE otherwise.
glEdgeFlag (GLboolean flag)表示一個頂點是否應該被認為是多邊形的一條邊界邊的起點。如果當前flag值為true,這個節點就被標記為邊界的起點。否則,該節點就被標記為非邊界的起點。
flag : Specifies the current edge flag value, either GL_TRUE or GL_FALSE. The initial value is GL_TRUE.
flag : 指定當前邊的標記值,為GL_TRUE或GL_FALSE。初始值為GL_TRUE。flag為GL_TRUE后面的點都被認為是邊界上的點,flag為GL_FALSE則之后的點不是邊界上的點。
(2)實例:
1)用凸邊形堆砌凹邊形:
#include <GL/glut.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") #define false 0 #define true 1 void SetupRC() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glColor3f(.0f, 0.0f, 1.0f); } void RenderScene() { glClear(GL_COLOR_BUFFER_BIT); // OpenGL命令,使用線框模式 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //總體圖形是由6個三角形堆砌而成的凹邊形 glBegin(GL_TRIANGLES); //第一個三角形作為實例講解:在設為true的頂點都可以作為凹邊形邊界的起點,所以有兩條直線BC,CA。沒有AB直線的原因是A不能作為線的起點。 glEdgeFlag(false); glVertex2f(-20.0f, 0.0f);//A glEdgeFlag(true); glVertex2f(20.0f, 0.0f);//B glVertex2f(0.0f, 40.0f);//C glVertex2f(-20.0f, 0.0f); glVertex2f(-60.0f, -20.0f); glEdgeFlag(false); glVertex2f(-20.0f, -40.0f); glEdgeFlag(true); glVertex2f(-20.0f, -40.0f); glVertex2f(0.0f, -80.0f); glEdgeFlag(false); glVertex2f(20.0f, -40.0f); glEdgeFlag(true); glVertex2f(20.0f, -40.0f); glVertex2f(60.0f, -20.0f); glEdgeFlag(false); glVertex2f(20.0f, 0.0f); //中間的兩個三角形的邊統統不要 glVertex2f(-20.0f, 0.0f); glVertex2f(-20.0f, -40.0f); glVertex2f(20.0f, 0.0f); glVertex2f(-20.0f, -40.0f); glVertex2f(20.0f, -40.0f); glVertex2f(20.0f, 0.0f); glEdgeFlag(true);//這個函數的功能是恢復畫線到初始狀態 //結束繪制三角形 glEnd(); glFlush(); } void ChangeSize(GLsizei w, GLsizei h) { GLfloat nRange = 100.0f; GLfloat aspectRatio; 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(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(400, 300); glutCreateWindow("用glEdgeFlag函數和凸邊形拼接非凸多邊形"); glutDisplayFunc(RenderScene); glutReshapeFunc(ChangeSize); SetupRC(); glutMainLoop(); return 0; }
如果不堆砌的話效果是這樣的:(測試時不用注釋glEdgeFlag語句,只需將語句#define false 0改為#define false 1)
2)給上述實例增加動畫效果,並自定義顯示模式:
#include <GL/glut.h> #include <math.h> //用於存取獲取屏幕的高寬分辨率 GLint SCREEN_WIDTH = 0; GLint SCREEN_HEIGHT = 0; //初始狀態程序的窗口大小 GLint windowWidth = 400; GLint windowHeight = 300; //用於設置旋轉動畫 GLfloat xRotAngle = 0.0f; GLfloat yRotAngle = 0.0f; #define MODE_SOLID 1 #define MODE_LINE 2 #define MODE_POINTS 3 GLint iMode = MODE_SOLID;//多邊形的填充方式 GLboolean bEdgeFlag = GL_TRUE;//控制邊的顯示與否 //受支持的點大小范圍 GLfloat sizes[2]; //受支持的點大小增量 GLfloat step; //菜單回調函數 void processMenu(int value){ switch (value){ case 1: iMode = MODE_SOLID; break; case 2: iMode = MODE_LINE; break; case 3: iMode = MODE_POINTS; break; case 4: bEdgeFlag = GL_TRUE; break; case 5: bEdgeFlag = GL_FALSE; break; default: break; } //重新繪制 glutPostRedisplay(); } //顯示回調函數 void renderScreen(void){ GLfloat x, y, z, angle; int i; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(xRotAngle, 1.0f, 0.0f, 0.0f); glRotatef(yRotAngle, 0.0f, 1.0f, 0.0f); //設置多邊形正面和背面的填充模式 if (MODE_SOLID == iMode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); if (MODE_LINE == iMode) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (MODE_POINTS == iMode) glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); x = 0.0f; y = 0.0f; z = 0.0f; //進行平滑處理 glEnable(GL_POINT_SMOOTH); glHint(GL_POINT_SMOOTH, GL_NICEST); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH, GL_NICEST); glEnable(GL_POLYGON_SMOOTH); glHint(GL_POLYGON_SMOOTH, GL_NICEST); //繪制坐標系 if (MODE_POINTS != iMode){ glColor3f(1.0f, 1.0f, 1.0f); glBegin(GL_LINES); glVertex3f(-80.0f, 0.0f, 0.0f); glVertex3f(80.0f, 0.0f, 0.0f); glVertex3f(0.0f, -80.0f, 0.0f); glVertex3f(0.0f, 80.0f, 0.0f); glVertex3f(0.0f, 0.0f, -80.0f); glVertex3f(0.0f, 0.0f, 80.0f); glEnd(); glColor3f(1.0, 0.0, 0.0); glPushMatrix(); glTranslatef(80.0f, 0.0f, 0.0f); glRotatef(90.0f, 0.0f, 1.0f, 0.0f); glutWireCone(3, 6, 10, 10); glPopMatrix(); glPushMatrix(); glTranslatef(0.0f, 80.0f, 0.0f); glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); glutWireCone(3, 6, 10, 10); glPopMatrix(); glPushMatrix(); glTranslatef(0.0f, 0.0f, 80.0f); glRotatef(90.0f, 0.0f, 0.0f, 1.0f); glutWireCone(3, 6, 10, 10); glPopMatrix(); glColor3f(1.0f, 1.0f, 1.0f); } glColor3f(0.0f, 0.0f, 0.5f); glBegin(GL_TRIANGLES); glPointSize(sizes[1]); glEdgeFlag(bEdgeFlag); glVertex2f(-20.0f, 20.0f); glEdgeFlag(GL_TRUE); glVertex2f(20.0f, 20.0f); glVertex2f(0.0f, 60.0f); glVertex2f(-20.0f, 20.0f); glVertex2f(-60.0f, 0.0f); glEdgeFlag(bEdgeFlag); glVertex2f(-20.0f, -20.0f); glEdgeFlag(GL_TRUE); glVertex2f(-20.0f, -20.0f); glVertex2f(0.0f, -60.0f); glEdgeFlag(bEdgeFlag); glVertex2f(20.0f, -20.0f); glEdgeFlag(GL_TRUE); glVertex2f(20.0f, -20.0f); glVertex2f(60.0f, 0.0f); glEdgeFlag(bEdgeFlag); glVertex2f(20.0f, 20.0f); glEdgeFlag(GL_TRUE); glEdgeFlag(bEdgeFlag); glVertex2f(-20.0f, 20.0f); glVertex2f(-20.0f, -20.0f); glVertex2f(20.0f, 20.0f); glVertex2f(-20.0f, -20.0f); glVertex2f(20.0f, -20.0f); glVertex2f(20.0f, 20.0f); glEdgeFlag(GL_TRUE); glEnd(); glPopMatrix(); glutSwapBuffers(); } void setupRC(void){ glClearColor(0.4f, 0.4, 0.4, 1.0f); glColor3f(0.0f, 1.0f, 0.0f); //使能深度測試,深度測試開啟之后能夠透過物體看到其他物體的色彩 glEnable(GL_DEPTH_TEST); //獲取受支持的點大小范圍 glGetFloatv(GL_POINT_SIZE_RANGE, sizes); //獲取受支持的點大小增量 glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step); printf("point size range:%f-%f\n", sizes[0], sizes[1]); printf("point step:%f\n", step); } void changSize(GLint w, GLint h){ GLfloat ratio; //設置坐標系范圍大小為x(-100.0f,100.0f)、y(-100.0f,100.0f)、z(-100.0f,100.0f) GLfloat coordinatesize = 100.0f; if ((w == 0) || (h == 0)) return;//就是使窗體改變操作無效 glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); ratio = (GLfloat)w / (GLfloat)h; if (w<h) glOrtho(-coordinatesize, coordinatesize, -coordinatesize / ratio, coordinatesize / ratio, -coordinatesize, coordinatesize); else glOrtho(-coordinatesize*ratio, coordinatesize*ratio, -coordinatesize, coordinatesize, -coordinatesize, coordinatesize); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void specialKey(int key, int x, int y){ if (key == GLUT_KEY_UP){ xRotAngle -= 5.0f; } else if (key == GLUT_KEY_DOWN){ xRotAngle += 5.0f; } else if (key == GLUT_KEY_LEFT){ yRotAngle -= 5.0f; } else if (key == GLUT_KEY_RIGHT){ yRotAngle += 5.0f; } glutPostRedisplay(); } int main(int argc, char* argv[]) { int nModeMenu; int nEdgeMenu; int nMainMenu; glutInit(&argc, argv); //使用雙緩沖區模式(動畫),深度緩沖(3D)和帶有透明度值得色彩設置 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); //獲取系統的高度和寬度分辨率 SCREEN_WIDTH = glutGet(GLUT_SCREEN_WIDTH); SCREEN_HEIGHT = glutGet(GLUT_SCREEN_HEIGHT); //創建二級菜單 nModeMenu = glutCreateMenu(processMenu); glutAddMenuEntry("Solid", 1); glutAddMenuEntry("Outline", 2); glutAddMenuEntry("Points", 3); nEdgeMenu = glutCreateMenu(processMenu); glutAddMenuEntry("On", 4); glutAddMenuEntry("Off", 5); //創建一級菜單 nMainMenu = glutCreateMenu(processMenu); glutAddSubMenu("Mode", nModeMenu); glutAddSubMenu("Edge", nEdgeMenu); //這兩個函數的順序不能調換 glutCreateWindow("3D空間中繪制凹邊形"); //將菜單榜定到鼠標右鍵上 glutAttachMenu(GLUT_RIGHT_BUTTON); printf("窗口的顯示寬度=%d,高度=%d\n", SCREEN_WIDTH, SCREEN_HEIGHT); /*這兩套語句具有相同的功能:初始化窗體的位置和大小,但一個放在創建窗體之前,一個放在其后 glutInitWindowSize(windowWidth,windowHeight); glutInitWindowPosition((1536 - 400) / 2,(864 - 300) / 2 ); */ glutPositionWindow((SCREEN_WIDTH - windowWidth) / 2, (SCREEN_HEIGHT - windowHeight) / 2); glutReshapeWindow(windowWidth, windowHeight); glutReshapeFunc(changSize); glutSpecialFunc(specialKey); glutDisplayFunc(renderScreen); setupRC();//RC=RederingState glutMainLoop(); return 0; }
3)2D球體反彈動畫
#include <GL/glut.h> #include <math.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") #define PI 3.1416 //方塊的起始位置和大小 GLfloat xIndex = 0.0f; GLfloat yIndex = 0.0f; GLfloat rsize = 25.0f; // 在x和y方向的步進大小 GLfloat xstep = 1.0f; GLfloat ystep = 1.0f; // 窗口的大小(使用邏輯笛卡爾坐標系統) GLfloat windowWidth; GLfloat windowHeight; void RenderScene() { int i; GLfloat x, y; glClear(GL_COLOR_BUFFER_BIT); /* 把當前繪圖顏色設置為紅色 glColor3f(1.0f, 0.0f, 0.0f); OpenGL命令,用當前的繪圖顏色繪制一個填充矩形(提供左上角和右下角的頂點坐標) glRectf(xIndex, yIndex, xIndex + rsize, yIndex - rsize);*/ glColor3f(1.0f, 0.0f, 1.0f); glBegin(GL_POLYGON); for (i = 0; i < 1000;i++) { x = 12.5*sin(2 * PI*i / 1000)+xIndex+12.5; y = 12.5*cos(2 * PI*i / 1000)+yIndex-12.5; glVertex2f(x,y); } glEnd(); glutSwapBuffers(); } //動畫的效果通過改變繪制圖形的位置實現的 // 由GLUT函數庫調用,計時器函數 void TimerFunction(int value) { // 在到達右邊或者左邊時翻轉方向,這里的邊界選取要結合裁剪區域的定義 if (xIndex >windowWidth - rsize || xIndex < -windowWidth) { xstep = -xstep; } // 在到達上邊或者下邊時翻轉方向,這里的邊界選取要結合裁剪區域的定義 if (yIndex >windowHeight || yIndex < -windowHeight + rsize) { ystep = -ystep; } // 移動方塊 xIndex += xstep; yIndex += ystep; // 檢查邊界,防止方塊在反彈時窗口變小,使方塊出現在新的裁剪區域之外 if (xIndex >(windowWidth - rsize + xstep)) { xIndex = windowWidth - rsize - 1; } else if(xIndex < -windowWidth - xstep) { xIndex = -windowWidth - 1; } // 檢查邊界,防止方塊在反彈時窗口變小,使方塊出現在新的裁剪區域之外 if (yIndex >(windowHeight + ystep)) { yIndex = windowHeight - 1; } else if(yIndex < -windowHeight + rsize - ystep) { yIndex = -windowHeight + rsize - 1; } glutPostRedisplay(); glutTimerFunc(20, TimerFunction, 1); } void SetupRC() { glClearColor(0.5f, 0.5f, 0.5f, 1.0f); } // 當窗口大小改變時由GLUT函數庫調用 void ChangeSize(GLsizei w, GLsizei h) { GLfloat aspectRatio; if (0 == h){ h = 1; } glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); aspectRatio = (GLfloat)w / (GLfloat)h; if (w <= h) { windowWidth = 100.0; windowHeight = 100.0 / aspectRatio; glOrtho(-100.0, 100.0, -100 / aspectRatio, 100 / aspectRatio, 1.0, -1.0); } else if(w>h) { windowWidth = 100.0 * aspectRatio; windowHeight = 100.0; glOrtho(-100.0 * aspectRatio, 100.0 *aspectRatio, -100.0, 100.0, 1.0, -1.0); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(480, 320); glutInitWindowPosition(400,300); glutCreateWindow("2D反彈動畫"); glutDisplayFunc(RenderScene); glutReshapeFunc(ChangeSize); // 設置計時器函數 glutTimerFunc(20, TimerFunction, 1); SetupRC(); glutMainLoop(); return 0; }
3)在平面中實現動態畫線:(實際上相當於把每條直線分為若干個直線,每繪制一小段直線進程陷入睡眠一段時間,看起來好像是動態效果。)
#include<GL/glut.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void init(void) { glClearColor(0.5, 0.5, 0.5, 0.5); glShadeModel(GL_FLAT); } /* 特別注意: 1、斜率不存在。2、直線斜率一定要保持一致,將直線兩點式方程轉化為斜率式方程y=kx+c k=(y2-y1)/(x2-x1); c=(x2*y1-x1*y2)/(x2-x1); */ //此函數用來畫一條自定義起始點顏色的線,自定義線的類型並且能夠實現動態繪制的功能 void DrawDynamicLine(GLfloat x1, GLfloat y1,GLfloat x2, GLfloat y2, GLfloat red, GLfloat green, GLfloat blue,int speed,int type) { glEnable(GL_LINE_STIPPLE);//點畫線模式 glColor3f(red, green, blue); glLineStipple(1, type);//dashed(虛線),type為16位2進制數,0表示實點,1表示虛點 if (x1 != x2)//如果斜率存在 { GLfloat k = (y2 - y1) / (x2 - x1); //直線斜率 y=kx+c GLfloat c = (x2*y1 - x1*y2) / (x2 - x1);//直線常數 //假定以A為原點建立二維坐標系,則下邊4個if分別對應於:第一象限,第二象限,第三象限,第四象限 if (x1<x2&&y1 <= y2) { for (int i = 0; i <= x2 - x1; i++) { glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x1 + i, k*(x1 + i) + c); glEnd(); Sleep(100 - speed); glFlush(); } } else if (x1>x2&&y1 <= y2) { for (int i = 0; i >= x2 - x1; i--) { glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x1 + i, k*(x1 + i) + c); glEnd(); Sleep(100 - speed); glFlush(); } } else if (x1>x2&&y1 >= y2) { for (int i = 0; i >= x2 - x1; i--) { glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x1 + i, k*(x1 + i) + c); glEnd(); Sleep(100 - speed); glFlush(); } } else if (x1<x2&&y1 >= y2) { for (int i = 0; i <= x2 - x1; i++) { glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x1 + i, k*(x1 + i) + c); glEnd(); Sleep(100 - speed); glFlush(); } } } else { if (y1<y2) { for (int i = 0; i <= y2 - y1; i++) { glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x1, y1 + i); glEnd(); Sleep(100 - speed); glFlush(); } } else { for (int i = 0; i >= y2 - y1; i--) { glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x1, y1 + i); glEnd(); Sleep(100 - speed); glFlush(); } } } } void display(void) { glClear(GL_COLOR_BUFFER_BIT); //清除所有的像素 //正方形ABCD DrawDynamicLine(100, 500, 500, 500, 1, 1, 0, 80, 0xFFFF); //AB DrawDynamicLine(500, 500, 500, 100, 1, 0, 1, 85, 0xFFFF); //BC DrawDynamicLine(500, 100, 100, 100, 1, 1, 1, 90, 0xFFFF); //CD DrawDynamicLine(100, 100, 100, 500, 0, 1, 1, 95, 0xFFFF); //DA //沿順時針方向測試動態畫線:第一象限,第四象限,第三象限,第二象限 DrawDynamicLine(300, 300, 300, 500, 1, 0, 0, 95, 0xFFFF); //1 DrawDynamicLine(300, 300, 400, 500, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 500, 500, 1, 0, 0, 95, 0xFFFF); DrawDynamicLine(300, 300, 500, 400, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 500, 300, 1, 0, 0, 95, 0xFFFF); DrawDynamicLine(300, 300, 500, 300, 1, 0, 0, 95, 0xFFFF); //2 DrawDynamicLine(300, 300, 500, 200, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 500, 100, 1, 0, 0, 95, 0xFFFF); DrawDynamicLine(300, 300, 400, 200, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 400, 100, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 300, 100, 1, 0, 0, 95, 0xFFFF); //3 DrawDynamicLine(300, 300, 200, 100, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 100, 100, 1, 0, 0, 95, 0xFFFF); DrawDynamicLine(300, 300, 100, 200, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 100, 300, 1, 0, 0, 95, 0xFFFF); DrawDynamicLine(300, 300, 100, 300, 1, 0, 0, 95, 0xFFFF); //4 DrawDynamicLine(300, 300, 100, 400, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 100, 500, 1, 0, 0, 95, 0xFFFF); DrawDynamicLine(300, 300, 200, 500, 1, 0, 0, 95, 0x00FF); DrawDynamicLine(300, 300, 300, 500, 1, 0, 0, 95, 0xFFFF); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); //為了選擇一個更小的繪圖區域,在窗口中定義一個像素矩形,將圖像映射到這個矩形中 glMatrixMode(GL_PROJECTION); //指定哪一個矩陣是當前矩陣(GL_PROJECTION,對投影矩陣應用隨后的矩陣操作) glLoadIdentity(); //將當前的用戶坐標系的原點移到了屏幕中心:類似於一個復位操作 gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h); //將當前的可視空間設置為正投影空間,這個函數描述了一個平行修剪空間,意味着離觀察者較遠的對象看上去不會變小 } int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(600, 600); glutInitWindowPosition(300, 100); glutCreateWindow("演示OpenGL圖形繪制過程"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
(3)相關知識:
1)獲取顯示器寬和高:
int glutGet(GLenum state)glutGet retrieves simple GLUT state represented by integers.檢索指定的GLUT狀態。
Description:
glutGet retrieves simple GLUT state represented by integers. The state parameter determines what type of state to return. Window capability state is returned for the layer in use. GLUT state names beginning with GLUT_WINDOW_ return state for the current window. GLUT state names beginning with GLUT_MENU_ return state for the current menu. Other GLUT state names return global state. Requesting state for an invalid GLUT state name returns negative one.
state為指定要檢索的狀態類型,為以下常量:
GLUT_WINDOW_X 當前窗口的x坐標,以像素為單位
GLUT_WINDOW_Y 當前窗口的y坐標,以像素為單位
GLUT_WINDOW_WIDTH 當其窗口的寬度,以像素為單位
GLUT_WINDOW_HEIGHT 當前窗口的高度,以像素為單位
GLUT_WINDOW_BUFFER_SIZE 當前窗口中,顏色分量占用的位數,即用多少bit表示顏色分量
GLUT_WINDOW_STENCIL_SIZE 當前窗口中,蒙板分量占用的位數,即用多少bit表示蒙板分量
GLUT_WINDOW_DEPTH_SIZE 當前窗口中,深度分量占用的位數,即用多少bit表示深度分量
GLUT_WINDOW_RED_SIZE 當前窗口中,紅色分量占用的位數,即用多少bit表示紅色分量
GLUT_WINDOW_GREEN_SIZE 當前窗口中,綠色分量占用的位數,即用多少bit表示綠色分量
GLUT_WINDOW_BLUE_SIZE 當前窗口中,藍色分量占用的位數,即用多少bit表示藍色分量
GLUT_WINDOW_ALPHA_SIZE 當前窗口中,alpha色分量占用的位數,即用多少bit表示alpha色分量
GLUT_WINDOW_ACCUM_RED_SIZE 當前窗口累積緩存中,紅色分量占用的位數,即用多少bit表示紅色分量
GLUT_WINDOW_ACCUM_GREEN_SIZE 當前窗口累積緩存中,綠色分量占用的位數,即用多少bit表示綠色分量
GLUT_WINDOW_ACCUM_BLUE_SIZE 當前窗口累積緩存中,藍色分量占用的位數,即用多少bit表示藍色分量
GLUT_WINDOW_ACCUM_ALPHA_SIZE 當前窗口累積緩存中,alpha色分量占用的位數,即用多少bit表示alpha色分量
GLUT_WINDOW_DOUBLEBUFFER 如果窗口式雙緩存模式,返回1,否則返回0
GLUT_WINDOW_RGBA 如果窗口是RGBA模式,返回1,否則返回0
GLUT_WINDOW_PARENT 查詢當前窗口的父窗口個數,如果為頂層窗口返回0
GLUT_WINDOW_NUM_CHILDREN 查詢當前窗口的子窗口個數
GLUT_WINDOW_NUM_SAMPLES 查詢多重采樣的采樣點個數
GLUT_WINDOW_STEREO 查詢是否使用立體模式,是則返回1,否則返回0
GLUT_WINDOW_CURSOR 返回光標的整數標示
GLUT_SCREEN_HEIGHT 屏幕的高度,以像素為單位 GLUT_SCREEN_WIDTH 屏幕的寬度,以像素為單位
GLUT_SCREEN_WIDTH_MM 屏幕的寬度,以毫米為單位
GLUT_SCREEN_HEIGHT_MM 屏幕的高度,以毫米為單位
GLUT_MENU_NUM_ITEMS 查詢當前菜單包含的菜單項的個數
GLUT_DISPLAY_MODE_POSSIBLE 查詢窗口系統是否支持當前的顯示模式,1表示支持,0表示不支持
GLUT_INIT_DISPLAY_MODE 初始窗口的顯示模式
GLUT_INIT_WINDOW_X 初始窗口的x坐標
GLUT_INIT_WINDOW_Y 初始窗口的y坐標
GLUT_INIT_WINDOW_WIDTH 初始窗口的寬度
GLUT_INIT_WINDOW_HEIGHT 初始窗口的高度
GLUT_ELAPSED_TIME 返回兩次調用glutGet(GLUT_ELAPSED_TIME)的時間間隔,單位為毫秒
2)添加菜單:
創建彈出式菜單的三個步驟: 1.定義菜單內各菜單項 2.定義每個菜單項的行為 3.把菜單關接到鼠標按鈕上
1.int glutCreateMenu(void(*func)(int value)
創建一個新的彈出式菜單並返回一個唯一標識此菜單的整型表示符。func指明此菜單的功能。
2.int glutAddMenuEntry(char *name,int value)
在當前菜單底部增加一個菜單條目。name指定顯示在新菜單條目上的ASCII碼字符串。 value指定當選擇該菜單條目時傳遞到菜單回調函數中的數值。
3.void glutAddSubMenu(char *name,int menu)
在當前菜單的底部增加一個子菜單觸發條目。在當前菜單的底部增加一個子菜單觸發條目。 name指定顯示在新菜單觸發條目上的ASCII碼字符串。 meun當選擇該子菜單觸發條目時彈出的子菜單的標識符。
4.void glutAttachMenu(int button)
把當前窗口的一個鼠標按鍵與當前菜單的標識符聯系起來。 button指明鼠標的哪個按鍵。GLUT_LEFT_BUTTON、GLUT_MIDDLE_BUTTON及GLUT_RIGHT_BUTTON,分別表明鼠標左、中及右鍵。
5.int glutGetMenu(void)
獲取當前菜單的標識符,如果沒有菜單存在或前一個當前菜單被刪除了,glutGetMenu則返回0值。
3)計時器:
glutTimerFunc(unsigned int millis, void (*func)(int value), int value)
三個參數分別為毫秒數, 回調函數指針, 區別值。區別值用於寫自己的回調函數:void OnTimer(int value)可以用value值區分是哪個定時器。
使用方法:
1.在函數里改變和位置有關的變量; 2.然后調用glutPostRedisplay(); 3.用來重繪最后再次調用glutTimerFunc;
4.函數末尾再次調用gluTimerfunc();
因為glut的定時器是調用一次才產生一次定時,所以如果要持續產生定時的話,在定時函數末尾再次調用glutTimerFunc。
4)進程睡眠:
Sleep函數:void Sleep(DWORD dwMilliseconds);
功能: 執行掛起一段時間
注意:在VC中使用帶上頭文件 #include <windows.h>
Sleep()單位為毫秒,sleep()單位為秒;unsigned sleep(unsigned seconds);
usleep單位為微秒;
返回值:若進程/線程掛起到參數所指定的時間則返回0,若有信號中斷則返回剩余秒數。
(4)在3D空間中動態畫線:(一次次重復地繪制全部的場景,每次場景僅比上次多一個小短邊。)
#include <GL/glut.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") GLfloat xRotAngle = 0.0f; GLfloat yRotAngle = 30.0f; GLint flag = 0; GLint temp = 0; void draw3DLine(GLfloat xBegin,GLfloat yBegin,GLfloat zBegin,GLfloat xEnd,GLfloat yEnd,GLfloat zEnd,GLint type) { //glLineStipple(1, type);//dashed(虛線),type為16位2進制數,0表示實點,1表示虛點(交替畫虛線和實線) GLint i = 0; GLfloat xStep, yStep, zStep; xStep = (xEnd-xBegin) / 500; yStep = (yEnd-yBegin) / 500; zStep = (zEnd-zBegin) / 500; glPointSize(2.0); glColor3f(0.0,0.0,1.0f); glEnable(GL_LINE_STIPPLE);//點畫線模式 for (i = 0; i < 500;i++) { glBegin(GL_LINES); glVertex3f(xBegin,yBegin,zBegin+i*zStep); glVertex3f(xBegin + (i+1)*xStep, yBegin + (i+1)*yStep, zBegin + (i+1)*zStep); glEnd(); if (flag == temp){ flag++;//每一次只比前一次多畫一條短線 break; } temp++; } } void paintSingleAxis(GLfloat axisLength) { glLineWidth(2.0f); glColor3f(1.0f,1.0f,1.0f);//白色的軸 glPushMatrix(); glBegin(GL_LINES); glVertex3f(0.0,0.0,0.0); glVertex3f(0.0, axisLength, 0.0);//繪制初態為y軸,轉換為x軸和z軸需要旋轉 glEnd(); glLineWidth(2.0f); glTranslated(0.0,axisLength - 8,0.0);//繪制紅色的尖端 glColor3f(1.0,0.0,0.0); glPushMatrix(); glRotated(-90,1,0,0);//由z軸旋轉到y軸上,右手系 glutWireCone(1, 8, 20, 20); glPopMatrix(); glPopMatrix(); glFlush(); } void readerScreen() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-200.0, 200, -200.0, 200.0, -200, 200); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(45, 60, 30, 0.0, 30, 0.0, 0, 10, 0); glPushMatrix(); glRotatef(xRotAngle, 1.0f, 0.0f, 0.0f); glRotatef(yRotAngle, 0.0f, 1.0f, 0.0f); //繪制坐標軸 glPushMatrix(); glRotated(-90, 0.0, 0.0, 1.0);//旋轉和繪制的順序不能調換 paintSingleAxis(80);//x軸 glPopMatrix(); paintSingleAxis(80);//y軸 glPushMatrix(); glRotated(90, 1.0, 0.0, 0.0); paintSingleAxis(80);//z軸 glPopMatrix(); glPointSize(20); temp = 0; //停在temp==flag上。 draw3DLine( 50, 50,-50, 50, 50, 50, 1);//A(50,50,-50),B(50,50,50) draw3DLine( 50, 50, 50,-50, 50, 50, 1); draw3DLine(-50, 50, 50,-50, 50,-50, 1);//C(-50,50,50),D(-50,50,-50) draw3DLine(-50, 50,-50, 50, 50,-50, 1); draw3DLine( 50,-50,-50, 50,-50, 50, 1);//E(50,-50,-50),F(50,-50,50) draw3DLine( 50,-50, 50,-50,-50, 50, 1); draw3DLine(-50,-50, 50,-50,-50,-50, 1);//G(-50,-50,50),H(-50,-50,-50) draw3DLine(-50,-50,-50, 50,-50,-50, 1); draw3DLine(-50, 50,-50,-50,-50,-50, 1); draw3DLine(-50, 50, 50,-50,-50, 50, 1); draw3DLine( 50, 50, 50, 50,-50, 50, 1); draw3DLine( 50, 50,-50, 50,-50,-50, 1); glPopMatrix(); glutSwapBuffers(); } void setupRC(void) { glClearColor(0.2,0.2,0.2,1.0); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); } // 當窗口大小改變時由GLUT函數庫調用 void ChangeSize(GLsizei w, GLsizei h) { GLfloat aspectRatio; if (0 == h){ h = 1; } glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); aspectRatio = (GLfloat)w / (GLfloat)h; if (w <= h) { glOrtho(-200.0, 200.0, -200 / aspectRatio, 200 / aspectRatio, 200, -200); } else if (w>h) { glOrtho(-200.0 * aspectRatio, 200.0 *aspectRatio, -200.0, 200.0, 200, -200); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void specialKey(int key, int x, int y) { if (key == GLUT_KEY_UP){ xRotAngle -= 5.0f; } else if (key == GLUT_KEY_DOWN){ xRotAngle += 5.0f; } else if (key == GLUT_KEY_LEFT){ yRotAngle -= 5.0f; } else if (key == GLUT_KEY_RIGHT){ yRotAngle += 5.0f; } glutPostRedisplay(); } void IdleFunction() { glutPostRedisplay(); Sleep(12-flag*0.002);//用來盡量保持勻速畫線 } int main(int argc,char *argv[]) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB); glutInitWindowSize(400,400); glutInitWindowPosition(200,100); glutCreateWindow("在3D空間中動態繪制"); glutDisplayFunc(readerScreen); glutSpecialFunc(specialKey); setupRC(); glutIdleFunc(IdleFunction); glutMainLoop(); return 0; }