本節是OpenGL學習的第五個課時,下面介紹OpenGL邊的相關知識:
(1)邊的概念:
數學上的直線沒有寬度,但OpenGL的直線則是有寬度的。同時,OpenGL的直線必須是有限長度,而不是像數學概念那樣是無限的。可以認為,OpenGL的“直線”概念與數學上的“線段”接近,它可以由兩個端點來確定。
(2)如何繪制邊:
1)OpenGL支持繪制三種類型的邊:
GL_LINES :指定兩個頂點,在它們之間繪制一條直線。如果為GL_LINES指定了奇數個頂點,那么最后一個頂點會被忽略。
GL_LINE_STRIP :線帶,它允許指定一個頂點列表,並繪制一條經過所有這些頂點的連續的線。
GL_LINE_LOOP:線環,它與線帶非常類似,會在頂點列表的最后一個頂點和第一個頂點之間也繪制一條直線。
2)線的另外幾種特點:
1.直線可以指定寬度:
void glLineWidth(GLfloat width);和自定義點的大小函數glPointSize()函數類似,在glBegin()函數之前調用。
2.可以畫實線也可以畫虛線:
1.使用glEnable(GL_LINE_STIPPLE)來啟動虛線模式。(使用glDisable(GL_LINE_STIPPLE)可以關閉之)。 2.使用glLineStipple來設置虛線的樣式。
函數void glLineStipple(GLint factor, GLushort pattern)用於在OpenGL中設置直線的當前點畫模式。
pattern參數是由1或0組成的16位序列,它們根據需要進行重復,對一條特定的直線進行點畫處理。從這個模式的低位開始,一個像素一個像素的進行處理。如果模式中對應的位是1,就繪制這個像素,否則就不繪制。模式可以使用factor參數(表示重復因子)進行擴展,它與1和0的連續子序列相乘。
GL_LINE_STIPPLE為參數調用glEnable()才能啟用直線點畫功能,相應的glDisable()出入此參數來關閉點畫功能。下面是講解示例:
兩行代碼:
glLineStipple(1, Ox3F07); glEnable(GL_LINE_STIPPLE); 此時模式為Ox3F07(二進制形式為0011111100000111),它所畫出來的直線是這樣的:先連續繪制3個像素,然后連續5個像素留空,再連續繪制6個像素,最后兩個像素留空(注意,首先是從右側低位開始的)。如果factor是2,那么這個模式便被擴展為:先連續繪制6個像素,然后連續10個像素留空,再連續繪制12個像素,最后4個像素留空。 如果沒有啟用點畫線功能,OpenGL會自動把pattern當做為OxFFFF,把factor當成1。
3)直線,線環,線帶的對比和虛線和實線的對比:
#include <GL/glut.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void paint(void) { glViewport(0,0,600,600); //先畫分割線,分為上下兩個區域,其中下區域再分為兩個。 glColor3f(1.0f,0.0f,0.0f); glLineWidth(2.0f); glBegin(GL_LINES); glVertex2f(-1.0f,0.0f); glVertex2f( 1.0f,0.0f); glVertex2f(0.0f,0.0f); glVertex2f(0.0f,-1.0f); glEnd(); glEnable(GL_LINE_STIPPLE); //在上方畫三條線 glViewport(0,300,600,300); glLineWidth(4.0f); glColor3f(1.0f, 1.0f, 1.0f); glLineStipple(1, 0xFFFF); glBegin(GL_LINES); glVertex2f(-1.0f,-0.5f); glVertex2f( 1.0f,-0.5f); glEnd(); glLineWidth(6.0f); glColor3f(0.0f, 1.0f, 1.0f); glLineStipple(2, 0x0F0F); glBegin(GL_LINES); glVertex2f(-1.0f, 0.0f); glVertex2f(1.0f, 0.0f); glEnd(); glLineWidth(8.0f); glColor3f(1.0f, 1.0f, 0.0f); glLineStipple(10, 0x0F0F); glBegin(GL_LINES); glVertex2f(-1.0f, 0.50f); glVertex2f(1.0f, 0.50f); glEnd(); glDisable(GL_LINE_STIPPLE); //在下方的左側畫線帶 glViewport(0,0,300,300); glColor3f(0.0f,1.0f,0.0f); glBegin(GL_LINE_STRIP); glVertex2f(-1.0f, 0.0f-0.2f); glVertex2f(-0.5f, 0.5f-0.2f); glVertex2f(-0.0f, 0.0f-0.2f); glVertex2f(0.5f, 0.5f-0.2f); glVertex2f(1.0f, 0.0f-0.2f); glEnd(); //在下方的右側畫線環 glViewport(300,0,300,300); glColor3f(0.0f,0.0f,1.0f); glBegin(GL_LINE_LOOP); glVertex2f(-1.0f, 0.0f+0.2f); glVertex2f(-0.5f, 0.5f+0.2f); glVertex2f(-0.0f, 0.0f+0.2f); glVertex2f(0.5f, 0.5f+0.2f); glVertex2f(1.0f, 0.0f+0.2f); glVertex2f(0.0f,-1.0f+0.2f); glEnd(); glFlush(); } void Init(void) { glClearColor(0.50f, 0.50f, 0.50f, 0.50f);//設置默認背景色要在清理顏色緩沖區之前 glClear(GL_COLOR_BUFFER_BIT); } int main(int argc, char *argv[]) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(615,600); glutInitWindowPosition(400,100); glutCreateWindow("各種直線的對比"); Init(); glutDisplayFunc(paint); glutMainLoop(); return 0; }
(3)調用OpenGL封裝好的函數來畫立方體線框:
#include <GL/glut.h> #include <stdlib.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); } void display(void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 0.0, 0.0); glLineWidth(2.5f); glLoadIdentity(); gluLookAt(1.0, 1.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glutWireCube(1.0); glFlush(); } //此函數能在窗口被拉伸時按比例拉伸我們畫的立方體框架 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-2.0, 2.0, -2.0, 2.0, 3.0, 40.0); glMatrixMode(GL_MODELVIEW); } int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(600, 400); glutInitWindowPosition(300, 100); //glutCreateWindow(argv[0]);argv[0]為默認的應用程序文件所在的路徑 glutCreateWindow("用直線畫立方體"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
1)代碼解釋:
1.void glShadeModel ( GLenum mode);
設置着色模式,參數一般為GL_SMOOTH或GL_FLAT。
GL_SMOOTH光滑着色模式:使用時,獨立的處理圖元中各個頂點的顏色。對於線段圖元,線段上各點的顏色將根據兩個頂點的顏色通過插值得到。對於多邊形圖元,多邊形內部區域的顏色將根據所有頂點的顏色插值得到。
GL_FLAT恆定着色模式,使用圖元中某個頂點的顏色來渲染整個圖元。
如果兩點的顏色相同,使用兩個參數效果相同。
平滑着色沒有明顯顏色的過度。
2.void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
第一組eyex, eyey,eyez 相機在世界坐標的位置
第二組centerx,centery,centerz 相機鏡頭對准的物體在世界坐標的位置
第三組upx,upy,upz 相機向上的方向在世界坐標中的方向
該函數定義了視點矩陣,並用該矩陣乘以當前矩陣。(http://cowboy.1988.blog.163.com/blog/static/751057982010101574732212/)
3.OpenGL中自帶繪制基本立體圖形的函數:
glutWireSphere繪制球
glutWireCone繪制椎體
glutWireCube繪制立體
glutWireTorus繪制甜圈
glutWireTeapot繪制茶壺
glutWireOctahedron繪制八面體
4.透視函數glFrustum(), gluPerspective()和glOrtho()的用法:
首先先介紹透視函數運用的背景:
在OpenGL中,如果想對模型進行操作,就要對這個模型的狀態(當前的矩陣)乘上這個操作對應的一個矩陣。
如果乘以變換矩陣(平移, 縮放, 旋轉), 那相乘之后, 模型的位置被變換;
如果乘以投影矩陣(將3D物體投影到2D平面), 相乘后, 模型的投影方式被設置;
如果乘以紋理矩陣(), 模型的紋理方式被設置.
glMatriMode(GLenum mode)用來指定乘以什么類型的矩陣; glMatrixMode有3種模式: GL_PROJECTION 投影, GL_MODELVIEW 模型視圖, GL_TEXTURE 紋理。
透視函數就運用在對模型進行投影操作這個過程中:
1.glMatrixMode(GL_PROJECTION); //將當前矩陣指定為投影矩陣 2.glLoadIdentity();然后把矩陣設為單位矩陣: 3.然后調用glFrustum()或gluPerspective(),它們生成的矩陣會與當前的矩陣相乘,生成透視的效果;
void glFrustum(GLdouble left, GLdouble Right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
此函數創建一個透視型的視景體。其操作是創建一個透視投影的矩陣,並且用這個矩陣乘以當前矩陣。
這個函數的參數只定義近裁剪平面的左下角點和右上角點的三維空間坐標,即(left,bottom,-near)和(right,top,-near);最后一個參數far是遠裁剪平面的離視點的距離值,其左下角點和右上角點空間坐標由函數根據透視投影原理自動生成。near和far表示離視點的遠近,它們總為正值(near/far 必須>0)。
void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);
創建一個對稱的透視型視景體,但它的參數定義於前面的不同。
參數fovy定義視野在Y-Z平面的角度,范圍是[0.0, 180.0];參數aspect是投影平面寬度與高度的比率;參數Near和Far分別是近遠裁剪面到視點(沿Z負軸)的距離,它們總為正值。
以上兩個函數缺省時,視點都在原點,視線沿Z軸指向負方向。
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
它創建一個平行視景體(就是一個長方體空間區域)。實際上這個函數的操作是創建一個正射投影矩陣,並且用這個矩陣乘以當前矩陣。
六個參數, 前兩個是x軸最小坐標和最大坐標,中間兩個是y軸,最后兩個是z軸值。其中近裁剪平面是一個矩形,矩形左下角點三維空間坐標是(left,bottom,-near),右上角點是(right,top,-near);遠裁剪平面也是一個矩形,左下角點空間坐標是(left,bottom,-far),右上角點是(right,top,-far)。
注意:所有的near和far值同時為正或同時為負, 值不能相同。如果沒有其他變換,正射投影的方向平行於Z軸,且視點朝向Z負軸。這意味着物體在視點前面時far和near都為負值,物體在視點后面時far和near都為正值。只有在視景體里的物體才能顯示出來。如果最后兩個值是(0,0),也就是near和far值相同了,視景體深度沒有了,整個視景體都被壓成個平面了,就會顯示不正確。
5.void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
The x and y parameters specify the lower-left corner of the viewport within the window, and the width and height parameters specify these dimensions in pixels. Usually, x and y are both 0, but you can use viewports to render more than one drawing in different areas of a window.
The viewport defines the area within the window in actual screen coordinates that OpenGL can use to draw in (see Figure 2.8). The current clipping volume is then mapped to the new viewport. If you specify a viewport that is smaller than the window coordinates, the rendering is scaled smaller, as you see in Figure 2.8.
The last requirement of our ChangeSize function is to redefine the clipping volume so that the aspect ratio remains square. The aspect ratio is the ratio of the number of pixels along a unit of length in the vertical direction to the number of pixels along the same unit of length in the horizontal direction. In English, this just means the width of the window divided by the height. An aspect ratio of 1.0 defines a square aspect ratio. An aspect ratio of 0.5 specifies that for every two pixels in the horizontal direction for a unit of length, there is one pixel in the vertical direction for the same unit of length. If you specify a viewport that is not square and it is mapped to a square clipping volume, the image will be distorted. For example, a viewport matching the window size and dimensions but mapped to a square clipping volume would cause images to appear tall and thin in tall and thin windows and wide and short in wide and short windows. In this case, our square would appear square only when the window was sized to be a square. In our example, an orthographic projection is used for the clipping volume. The OpenGL command to create this projection is glOrtho: void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far ); In 3D Cartesian space, the left and right values specify the minimum and maximum coordinate value displayed along the x-axis; bottom and top are for the y-axis. The near and far parameters are for the z-axis
glViewport是一塊畫布的大小,而gluOrtho(修剪空間)則定義了畫布的可視范圍。(我上面的例子也是很好的窗口裁剪實例,采用模型視景時多用此函數。)
2)相關知識:
1.模型變換與視圖變換:(http://www.cnblogs.com/opengl/archive/2012/11/06/2757854.html)
視圖變換,即模型(參照物)位置不變,但視圖在發生變化;
模型變換,即視圖位置觀察方式不變,模型發生變化。
2.坐標系:(http://blog.csdn.net/hunter8777/article/details/5890899)
openGL使用右手坐標從左到右,x遞增從下到上,y遞增從遠到近,z遞增。
OPENGL坐標系可分為:世界坐標系和當前繪圖坐標系:
世界坐標系以屏幕中心為原點(0, 0, 0)。你面對屏幕,你的右邊是x正軸,上面是y正軸,屏幕指向你的為z正軸。長度單位這樣來定: 窗口范圍按此單位恰好是(-1,-1)到(1,1)。 當前繪圖坐標系是繪制物體時的坐標系。 程序剛初始化時,世界坐標系和當前繪圖坐標系是重合的。當用glTranslatef(),glScalef(), glRotatef()對當前繪圖坐標系進行平移、伸縮、旋轉變換之后, 世界坐標系和當前繪圖坐標系不再重合。改變以后,再用glVertex3f()等繪圖函數繪圖時,都是在當前繪圖坐標系進行繪圖,所有的函數參數也都是相 對當前繪圖坐標系來講的。
OpenGL中存在6種坐標系:
1. Object or model coordinates 2. World coordinates 3. Eye (or Camera) coordinates 4. Clip coordinates 5. Normalized device coordinates 6. Window (or screen) coordinates
(4)在3D空間內繪制直線並動態查看:
#include <GL/glut.h> #include <math.h> #define GL_PI 3.1416f static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; void SetupRC() { glClearColor(0.0f, 0.0f, 0.0f, 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); glRotatef(yRot, 0.0f, 1.0f, 0.0f); // 開始繪制直線(圖元) glBegin(GL_LINES); // 設置Z等於零,繪制的直線完全位於X-Y平面 z = 0.0f; // 畫線 for (angle = 0.0f; angle <= GL_PI; angle += (GL_PI / 20.0f)) { // 圓的上半部分,計算X、Y坐標 x = 50.0f * sin(angle); y = 50.0f * cos(angle); // 指定頂點位置,直線的第一頂點 glVertex3f(x, y, z); // 圓的下半部分,計算X、Y坐標 x = 50.0f * sin(angle + GL_PI); y = 50.0f * cos(angle + GL_PI); // 指定頂點位置,直線的第二頂點,與第一頂點相對應 glVertex3f(x, y, z); } glEnd(); // 恢復矩陣狀態 glPopMatrix(); // 刷新繪圖命令,此時所有未執行的OpenGL命令被執行 glutSwapBuffers(); } 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(); } 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(); glutMainLoop(); return 0; }