




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第二部分 使用OpenGL 好像大学里的每一堂程序课都是从一个愚蠢的问题:你到纽约去一加仑油可以跑多少英里?这个例程开始的。首先你要学会用电脑;然后是编辑器;连接编译器;程序是怎样组织的;最后是一些语法知识。不幸的是,就像我们要学会走路之前必须学会爬一样,学OpenGL也没有例外。 本书的第一部分介绍了OpenGL,关于三维的一些为什么和怎样及OpenGL函数的格式。然后就是把这加入到Windows API中去,建立基于Windows的OpenGL程序并在客户区绘图。我们学会了怎检查错误,及怎样解释它们。现在是我们应从婴儿学步毕业并能到房间里东倒西歪的走路的时候了。首先在第六节中,我们将涵盖了
2、OpenGL绘制所有图元的内容。你将会使用这些建立更复杂一些的物体。其次,你会发现你可以三维空间中能做的所有的事情:变换、旋转、还有坐标变换等等。如果走得更自信,那么就你为第八节和第九节做好准备,在那里你将学到颜色、消隐和灯光,这样你就可以得到现实的效果了。剩下的章节提供一些高级物体控制的工具,技巧:扭曲图片和材质映射,和一些三维的物体图元。第六节 绘制三维物体:线、点、多边形在这一节中你将学会以下内容:How To你将用到的函数画点、线、形状glBegin/glEnd/glVertex设置形状的外表为线框或实体glPolygonMode设置绘制的点的大小glPointSizePerform
3、hidden surface removalglCullFace设置点划线的部分glLineStipple设置多边形填充部分glPolygonStipple如果你上过化学课,即使你没有上过,你知道所有的物质都是由原子组成的,一个原子又由三部分构成:质子、中子、电子。你遇到的所有的东西从一瓣玫瑰花到岸边的砂子,只是这三样东西的不同组合而已。尽管这对于一个三年级或四所级的学生来说是有点太简单了。但是我只是想说明一个有力的原理:只需要几个简单的建材单元,你就可以创造出复杂和漂亮的结构。它们的联系是相当明显的。你用OpenGL在场景中创建的物体也都是由一些小的、简单的用不同的方式安排组合在一起的。在这
4、节中,你将探索这些建立三维物体的单元,称为图元primitive。所有的OpenGL图元是一维或二维的物体,从一个点到线和多边形。在本节中你将学到在三维空间中绘图的所有知识。在三维空间画点当你开始在任何电脑系统中学习绘制图形时,一开始都是画点(像素)。一个像素是你显示器上最小的单元。在颜色系统中,一个点可以有任何显示器可提供的颜色。这就是计算机图形学最简单的例子:在显示器的一个位置画一个点,并将它设置为指定的颜色。然后基于这个观念,用你喜欢的计算机语言去画线、多边形、圆和其它的图形。甚至可以是一个GUI尽管在OpenGL中,在屏幕上画图有点不同。人不需要关注物理坐标系统和像素,但是要关注你的视
5、景体中的位置坐标 positional coordinates。你不需要管怎样得到你的点、线和从三维空间到你屏幕的二维空间的转换,都交给OpenGL去做吧。本节和下一节将介绍OpenGL三维图形工具套的基本观念。在接下来的章节中,我们将详细讲解三维空间到二维空间的变换,及怎样控制:旋转、平移、缩放物体。现在我们应该掌握这个,为发专心的在三维空间中绘图。这可能有点后退,但是如果你是最一次知道绘制一些东西,接下来的第七节的内容将会很有趣和简单易学。只要你对图元和坐标变换有了深刻理解,你就可以很快地掌握任何三维图形语言或者 API。准备好3D的画布图6-1所示为一个我们在本节的例子中即将使用的简单的
6、视景体。这个封闭的立方体是一个笛卡尔坐标空间,每个轴XYZ上都是从-100到100。(对笛卡尔坐标系的回顾请看第二节)。把这个视景体当成你的画布,你将在上面用OpenGL的命令绘图。图6-1 100*100*100的笛卡尔视景体 我们通过调用函数glOrtho()来建立这个视景体,就跟我们在先前的章节中一样。清单6-1所示为我们ChangeSize()函数的代码,当窗口尺寸改变时就会调用这个函数,包括第一次被创建时。这代码和我们原来使用的代码有点不同,你可能也注意到了一些你不熟悉的函数。(glMatrixMode,glLoadIdentity)我们将在第七节中详细讲解它们。清单6-1建立图6-
7、1所示的视景体的代码/ Change viewing volume and viewport. Called when window is resizedvoid ChangeSize(GLsizei w, GLsizei h) GLfloat nRange = 100.0f; / Prevent a divide by zero if(h = 0) h = 1; / Set Viewport to window dimensions glViewport(0, 0, w, h); / Reset projection matrix stack glMatrixMode(GL_PROJECTI
8、ON); glLoadIdentity(); / Establish clipping volume (left, right, bottom, top, near, far) if (w = h) glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange,nRange);else glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange,nRange); / Reset Model view matrix stack glMatrixMode(GL_MODELVIEW);
9、glLoadIdentity();一个三维的点为了在三维的空间中画出指定的点,我们使用函数glVertex,勿庸置疑,这是所有的OpenGLAPI中最常用的函数。这是OpenGL中最常见的图元:空间中的一个点。这个函数的参数可以从二个到四个的任意数据类型,从字节到实型,它们与名字有关。下面的一行代码指定了我们三维空间中的一个点:没X轴50个单位,沿Y轴50个单位,没Z轴为0。glVertex3f(50.0f,50.0f,0.0f);在图6-2中演示了这个点。这里我们选择显示一个实型的点。另外这个函数的参数分别为空间点的三个坐标值是XYZ。图6-2 函数glVertex3f(50.0f,50.0
10、f,0.0f)指定的点(50,50,0) 这个函数另外的格式分有别有两个参数的和四个参数的。我们也可以用函数glVertex2f(50.0f,50.0f)得到图6-2所示的一样的点。这种形式只有两个参数,它们指定了XY坐标的值,并设Z坐标值为0。四个参数的glVertex函数的第四个参数W是用于缩放的。你将在第七节中学到。画点什么 现在我们有了一个方法去OpenGL指定空间中的一个点。我们怎样去做到呢?我们怎样告诉OpenGL去做什么呢?这个点是不是要画的点?它是一个线的端点还是一个立方体的顶点呢?一个顶点的几何定义不只是空间中的一个点,而是两条线的交点。这是图元的本质。一个图元简单的解释就是
11、屏幕上所画的形状的一系列顶点。OpenGL中有十个图元。从空间中最简单的一个点到任意条边所围成的多边形。你使用glBegin命令告诉OpenGL开始绘制图元。使用glEnd命令来结束绘制。很直观吧,你认为呢?画点让我们从最简单的图元开始:点。看看如下代码:glBegin(GL_POINTS); /Select points as the primitiveglVertex3f(0.0f,0.0f,0.0f);/Specify a pointglVertex3f(50.0f,50.0f,50.0f);/Specify another pointglEnd();/Done drawing poin
12、ts函数glBegin()的参数:GL_POINTS告诉OpenGL下面的顶点是用来绘制点的。然后列出了两个点,它们转化为两个指定的点,都将在屏幕上绘制出来。这就说明了glBegin和glEnd的重要的一点:你可在调用它们时,在它们中间加入任意多个相同的图元。下面的代码就相当浪费了并执行缓慢:glBegin(GL_POINTS);glVertex3f(0.0f,0.0f,0.0f);glEnd();glBegin(GL_POINTS):glVertex3f(50.0f,50.0f,50.0f);glEnd();缩进你的代码在前一个例子中,你注意到调用glVertex3f()函数的缩进样式了没?
13、将种方式将会使你的OpenGL程序更易读。这不是必须的,但是这样做将会更容易的找到图元是从哪开始到哪结束的。我们的第一个例子清单6-2列出的为在我们的三维空间中画的一些点。它是利用三角学的知识绘制的沿Z轴螺旋上升的点。清单6-2 弹簧形点的程序代码/Draw a spring-shape path of points.#include #include #include #include #define GL_PI 3.14159f#define IDT_TIMER 101HGLRC hRC;HDC hDC;GLfloat xRot = 1.0f;GLfloat yRot = 1.0f;GLf
14、loat windowWidth;GLfloat windowHeight;LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);void SetupRC(void);void SetDCPixelFormat(HDC hDC);void CALLBACK RenderScene(void);void CALLBACK IdleFunction(void);void CALLBACK ChangeSize(GLsizei width,GLsizei height);/Entry point of all windows W
15、INAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)MSG msg;HWND hWnd;WNDCLASS wc;CHAR szAppName = Windows Version bouncing-eryar;CHAR szClassName = myOpenGLClass;/Register Window stylewc.style= CS_HREDRAW|CS_VREDRAW;wc.lpfnWndProc= WndProc;wc.cbClsExtra= 0;wc.cb
16、WndExtra= 0;wc.hInstance= hInstance;wc.hIcon= NULL;wc.hCursor= LoadCursor(NULL,IDC_ARROW);wc.lpszClassName= szClassName;wc.hbrBackground= NULL;/No need for background brush for OpenGL windowwc.lpszMenuName= NULL;if(!RegisterClass(&wc) MessageBox(NULL,Register class failed!,message,MB_OK);return FALS
17、E;hWnd = CreateWindow(szClassName,szAppName,WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,350,350,NULL,NULL,hInstance,NULL);if(!hWnd) MessageBox(NULL,Create Window failed!,message,MB_OK);return FALSE;ShowWindow(hWnd,nCmdShow);UpdateWindow(hWnd);while(GetMessage(&msg
18、,NULL,0,0) TranslateMessage(&msg);DispatchMessage(&msg);return msg.wParam;LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)switch(message) case WM_CREATE:/Window creation,setup for OpenGLhDC = GetDC(hWnd);/Store the device contextSetDCPixelFormat(hDC);/Select the pixel for
19、mathRC = wglCreateContext(hDC);/Create the rendering contextwglMakeCurrent(hDC,hRC);/and make it currentSetTimer(hWnd,IDT_TIMER,1,NULL);/Create a timer that fires every millisecondSetupRC();break;case WM_SIZE:ChangeSize(LOWORD(lParam),HIWORD(lParam);break;case WM_PAINT:RenderScene();SwapBuffers(hDC)
20、;ValidateRect(hWnd,NULL);break;case WM_TIMER:IdleFunction();InvalidateRect(hWnd,NULL,FALSE);break;case WM_KEYDOWN:switch(wParam) case VK_ESCAPE:if(MessageBox(hWnd,Are you sure?,Quit,MB_OKCANCEL)=IDOK)PostQuitMessage(0);break;break;case WM_DESTROY:KillTimer(hWnd,IDT_TIMER);wglMakeCurrent(hDC,NULL);Po
21、stQuitMessage(0);break;return DefWindowProc(hWnd,message,wParam,lParam);/Select the pixel format for a given device contextvoid SetDCPixelFormat(HDC hDC)int nPixelFormat;static PIXELFORMATDESCRIPTOR pfd = sizeof(PIXELFORMATDESCRIPTOR),1,PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,PFD_TYPE
22、_RGBA,24,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,PFD_MAIN_PLANE,0,0,0,0;/Choose a pixel format that best matches the described in pfdnPixelFormat = ChoosePixelFormat(hDC,&pfd);/Set the pixel format for the device contextSetPixelFormat(hDC,nPixelFormat,&pfd);void CALLBACK ChangeSize(GLsizei width,GLsizei he
23、ight)if(height = 0)height = 1;glViewport(0,0,width,height);glLoadIdentity();if(width=height) windowWidth = 250;windowHeight= (GLfloat)250*height/width;else windowWidth = (GLfloat)250*width/height;windowHeight= 250;glOrtho(-windowWidth,windowWidth,-windowHeight,windowHeight,-200.0f,200.0f);void Setup
24、RC(void)/Black backgroundglClearColor(0.0f,0.0f,0.0f,1.0f);glColor3f(0.0f,1.0f,0.0f);void CALLBACK RenderScene(void)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_POINTS);z = -50.0f;for(angle = 0.0f;angle=(2.0f
25、*GL_PI)*30.0f;angle+=0.1f) x = 50.0f*(GLfloat)sin(angle);y = 50.0f*(GLfloat)cos(angle);glVertex3f(x+50.0f,y+50.0f,z);z += 0.5f;glEnd();glPopMatrix();glFlush();void CALLBACK IdleFunction(void)xRot += 0.05f;yRot += 0.05f; 只有调用glBegin()和glEnd()函数中间的代码是重要的。这些代码计算了弹簧从0到360度三次的XY坐标。每当画完一个点后,Z的值就增加一点。当这个程序
26、运行时,你能看到只是一圈点,那是因为你最初是沿Z轴看的。为了得到好的效果,用光标来旋转这个物体。如图6-3所示:图6-3 设置点的大小当你画一个点时,默认的是一个像素。你可用此函数来改变点的大小。void glPointSize(GLfloat size);函数只有一个参数,用来指定被画点的大小。并不是所有的大小都支持,你需要确定你用的点的大小是可用的。用下面的函数来得到点的大小的范围和最小间隔。GLfloat sizes2;/Store supported point size rangeGLfloat step ;/Store supported point size increments
27、/Get supported point size range and step sizeglGetFloatv(GL_POINT_SIZE_RANGE,sizes);glGetFloatv(GL_POINT_GRANULARITY,&step);这里的数组sizes将包含两个内容:最小和最大可用点的大小值。另外,step变量将保存两个点之间的最小的增量。OpenGL只支持一种点的大小,那就是1。微软对OpenGL点的大小的支持从0.5到10.0。最小增量为0.125。指定在范围之外的点将不会认为是错误。相反地,最大的或最小的点将会被使用,因为它们最接近指定的值。让我们看看使用这几个新函数的例
28、子程序。如图6-4所示:图6-4 清单6-3 点的大小程序void CALLBACK RenderScene(void)GLfloat x,y,z,angle;GLfloat sizes2;GLfloat step;GLfloat curSize;glGetFloatv(GL_POINT_SIZE_RANGE,sizes);glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);curSize = sizes0;z = -90.0f;for(angle = 0.0f;angle=(2.0f*GL_PI)*2.0f;angle+=0.1f) x = 50.0f*
29、(GLfloat)sin(angle);y = 50.0f*(GLfloat)cos(angle);glPointSize(curSize);glBegin(GL_POINTS);glVertex3f(x,y,z);glEnd();z += 3.0f;curSize += step;glFlush(); 这个例子说明了几点重要的事情。对于初学者,调用glPointSize函数必须在glBegin/glEnd之外。glPointSize函数对后面所有点的大小都有影响。最明显的一点你可能已经从摘录的代码中发现,就是大点只是简单的显示为大的方块。这是默认的表现。你可能也会想,为什么你可以以小于一的数
30、来增加点的大小。如果1表示一个像素,那么小于1的点将会画成什么样子呢? 答案就是用函数glPointSize指定的点的大小并不是以像素来精确表示的点的大小,只是一个用来画点的圆的大约的半径。你也可以让OpenGL来画好看一点的点,调用glEnable(GL_POINT_SMOOTH);在三维空间中画线图元GL_POINTS相当老实,你指定一个点它就画一个点。下一步就是指定两个点,然后画它们之间的线。这就是图元GL_LINES。下面的代码画了一条线(0,0,0)(50,50,50)。glBegin(GL_LINES);glVertex3f(0.0f,0.0f,0.0f);glVertex3f(5
31、0.0f,50.0f,50.0f);glEnd();注意到这里两个点指定一个图元,画了一条线。如果你指定了一个没有配对的点,最后的一个点将被忽视。从CD中的例程LINES中提出的代码清单6-4显示了一个复杂一点的例子,用线画了一个扇子状的圆。如图6-5所示。图6-5 清单6-4 glBegin(GL_LINES);z = 0.0f;for(angle = 0.0f;angle=(2.0f*GL_PI)*2.0f;angle+=0.5f) x = 50.0f*(GLfloat)sin(angle);y = 50.0f*(GLfloat)cos(angle);glVertex3f(x,y,z);x
32、 = 50.0f*(GLfloat)sin(angle+GL_PI);y = 50.0f*(GLfloat)cos(angle+GL_PI);glVertex3f(x,y,z);glEnd();不闭合线条和闭合线条当你指定GL_LINE_STRIP时,将一个点到另一个点连续画线。下面的代码在XY平面上指定三个点画了两条线。效果图如图6-6所示:glBegin(GL_LINE_STRIP);glVertex3f(0.0f,0.0f,0.0f);/V0glVertex3f(50.0f,50.0f,0.0f); /V1glVertex3f(50.0f,100.0f,0.0f);/V2glEnd();
33、图6-6最后一个关于线的图元是GL_LINE_LOOP。它和图元GL_LINE_STRIP一样,唯一不同的是最后一条线连接的是第一个点和最后一个点。这使画一个封闭的图形变得容易。图6-6所示的同样的三个点用GL_LINE_LOOP显示不同的效果。图6-7 用直线去逼近曲线如图6-3所示程序POINTS,你可能想把点挨得越近(通过设置小的小角的增量)来逼近一条曲线来代替一个个间断的点。这是可行的操作,但是当曲线由成千上万个点组成时,这将很慢。逼近曲线好的方法就是用GL_LINE_STRIP来连接这些点。当这些点挨得越近时,一条曲线自然而然的就有了,不用你去指定这些点。清单6-5所示的为把清单6-
34、2中的GL_POINTS替换为GL_LINE_STRIP得到的。效果图如图6-8所示。你可以看到曲线还是相当不错的。你将会发现这个好用的技巧在OpenGL程序中无处不在。图6-8 清单6-5 glBegin(GL_LINE_STRIP);z = 0.0f;for(angle = 0.0f;angle=(2.0f*GL_PI)*10.0f;angle+=0.1f) x = 50.0f*(GLfloat)sin(angle);y = 50.0f*(GLfloat)cos(angle);glVertex3f(x,y,z);z += 0.5f;glEnd();设置线宽正如你可以设置不同的点的大小一样,
35、你也可以设置不同的线宽。由函数来完成:void glLineWidth(GLfloat width);函数glLineWidth只有一个参数,它用像素指定了线的大约宽度。就像点的大小一样,并不是所有的线宽都被支持,你需要确定你使用的线宽是可用的。使用下面的代码来得到线宽的范围和线宽之间的最小间隔。GLfloat sizes2;/Store supported line width rangeGLfloat step;/Store supported line width increments/Get supported line width range and step sizeglGetFl
36、oatv(GL_LINE_WIDTH_RANGE,sizes);glGetFloatv(GL_LINE_WIDTH_GRANULARITY,&step); 这里的数组sizes里的两个单元分别存放线宽的最大值和最小值。变量step中存放线宽间的最小间隔。清单6-6所示代码为关于线宽函数的例子。它是从程序LINESW中摘录的一部分,画了十种不同线宽的线。图6-9为程序的输出。图6-9清单6-6绘制不同线宽的直线void CALLBACK RenderScene(void)GLfloat y;/Storage for varying Y coordinameGLfloat fSizes2;/Lin
37、e width range metricsGLfloat fCurrSize;/Save current sizeglGetFloatv(GL_LINE_WIDTH_RANGE,fSizes);fCurrSize = fSizes0;/Stop up Y axis 20 units at a timefor(y = -90.0f;y90.0f;y+=20.0f) glLineWidth(fCurrSize);/Set the line widthglBegin(GL_LINES);/Draw the lineglVertex2f(-100.0f,y);glVertex2f(100.0f,y);
38、glEnd();fCurrSize += 1.0f;/Increase the line widthglFlush(); 注意到这次我们使用glVertex2f()来指定我们线中点的坐标。如前文所述,这是很方便的,因为我们只在XY平面上绘制。点划线如同设置线宽,你可以创建点线或短划线,称为点划线。为了使用点划线,你应先调用这个函数:glEnable(GL_LINE_STIPPLE);然后再调用glLineStipple建立要使用的点划线:void glLineStipple(Glint factor,GLushort pattern);提醒:通过调用glEnable()来使一种功能可用,调用g
39、lDisable()来使之不可用。参数pattern是一个16位的值,用来指定要使用点划线的原型。每一位表示一个线段中要亮或灭的部分。默认的是一个像素对应一个点;参数factor可以增加两个点之间的间隔。例如:设置factor为5,则不管点划线的原型是亮还是灭,它的每一行都占有5个像素的点或亮或灭。图6-10说明了一个简单的点划线原型的设置。图6-10 清单6-7所示为使用点划线的简单程序。如图6-11所示:图6-11清单6-7点划线程序void CALLBACK RenderScene(void)GLfloat y;/Storage for varying Y coordinameGLint
40、 factor = 1;/Stippling factorGLushort pattern = 0x5555;/Stipple patternglEnable(GL_LINE_STIPPLE);/Stop up Y axis 20 units at a timefor(y = -90.0f;y90.0f;y+=20.0f) glLineStipple(factor,pattern);glBegin(GL_LINES);/Draw the lineglVertex2f(-200.0f,y);glVertex2f(200.0f,y);glEnd();factor +;glFlush();在三维空间
41、中画三角形你已经知道怎样画点和线,及一些闭合的多边形(GL_LINE_LOOP)。只使用这几个图元你就可以轻松地绘出三维空间的任意图形。例如:你可以画六个方形并把它们设置为一个立方体的六个面从而得到一个立方体。你可能已经注意到了,用这些图元创建的形状没有填充任何颜色,终究你只是在画线。实际上,先前的所有例子只不过是画了一个立方体的线框架并不是一个实体。你需要比点和线更多的东西多边形(polygon)来画一个实体的表面。多边形是一个可以用当前选中的颜色来填充的封闭形状,它也是OpenGL中组成实体的基本单元。你的第一个多边形:三角形最简单的多边形可能是三角形了,因为它只有三条边。图元GL_TRI
42、ANGLES就可以画三角形,它是通过连接三个顶点来实现的。以下代码分别用三个点连接了两个三角形。如图6-12所示:图6-12 用GL_TRIANGLES绘制的两个三角形程序代码:glBegin(GL_TRIANGLES);glVertex2f(0.0f,0.0f);glVertex2f(25.0f,25.0f);glVertex2f(50.0f,0.0f);glVertex2f(-50.0f,0.0f);glVertex2f(-75.0f,50.0f);glVertex2f(-25.0f,0.0f);glEnd(); 注意到两个三角形将会被当前选中的颜色填充。如果不指定颜色,你对结果是不肯定的
43、,因为没有默认的绘图颜色。选择最快的图元的提示: 三角形是OpenGL程序员首选的建模图元。你将会发现只需很少的工作,任意一个多边形都可以由一个或多个三角形组成。很多3D加速硬件也对绘制三角形做了很大的优化。旋向Winding 图6-12所示的任意一个多边形具有的重要特征:注意连接两点的线上的箭头。当画第一个三角形时,线是这样画出来的:V0-V1然后是V2,最后是V0。这条路径是按指定点的顺序进行的,同时也是顺时针的。第二个三角形也是同样的方向。 指定点的顺序、方向的组合就是旋向Winding。我们说如图6-12所示的三角形是顺时针旋向的,因为它们从字面上来看的话的确是顺时针的。如果我们交换左
44、边三角形中V4和V5顶点的顺序,将会得到逆时针的旋向。如图6-13所示:图6-13 不同旋向的两个三角形 OpenGL默认的多边形旋向为逆时针的面作为前面。也就是说图6-13中左边的三角形显示的是前面,右边的三角形显示的是背面。 这有什么重要呢?你将会看到,通常你想绘一个多边形的前面和背面不同的物理特性。你可以隐藏背面,也可以给它以不同的颜色,或是不同的反射属性(第九节中将会讲到)。在同一个场景中用前面来绘制实体的表面,并把它们保持一致是非常重要的。在接下来的实体章节中,我们将会用复杂点的模型来演示这个概念。 如果你要换掉OpenGL默认的旋向,你可以调用glFrontFace(GL_CW);
45、参数GL_CW告诉OpenGL把多边形顺时针旋向的面作为前面。用GL_CCW换回把多边形逆时针旋向的面作为前面。三角形串许多表面和形状都是由连接在一起的三角形组成的。你可用GL_TRIANGLE_STRIP图元绘制三角形串并能节省时间。图6-14所示为用V0到V4五个顶点绘制三个三角形的过程。这里你看到的顶点没必要详细指定它们的顺序。因为默认的每个三角形的旋向是逆时针的。图6-14 相对单独指定每个三角形而言,使用三角形串strip of triangle有两大优势:第一,当指定第一个三角形的三个顶点之后,你只需为后面增加的三角形指定一个点。当你需要画很多三角形时这就节省了许多时间和数据空间;
46、第二,正如前文提到一样,用三角形组成物体或其表面相对用其它图元来表示而言是一个好主意。 另外一个优势就是:当场景中有灯光效果时,用小的三角形来组成大的平面,OpenGL模拟的效果会更好。我们将在第九节中学习这些技巧。扇形三角形串 你可以用GL_TRIANGLE_FAN来一组绕一个顶点扇形连接的三角形。图6-15所示为指定四个顶点组成的由三个三角形绕成的扇形。第一个顶点V0是扇形的原点。在开始的三个顶点组成最初的一个三角形以后,其后每一个点Vn及先前一个点Vn-1和原点V0组成下一个三角形。注意到每个三角形是顺时针的旋向。图6-15创建实体编辑由三角形或其它多边形组成我实体无非涉及到在三维空间中
47、组装点。让我们检查一下我们的例子程序TRIANGLE,用两个扇形三角形串组成了一个锥体。第一个扇形三角形串产生了锥体的锥面,另一个产生了锥体的底面。程序的输出如图6-16所示。这里你是沿着Z轴看过去的,所以只看到由三角形组成的扇形。每一个三角形都间隔用绿或红来强调了。图6-16 函数SetupRC()和RenderScene()的代码在清单6-8中给出了。这个程序演示了组成三维物体的几个方面。注意一下菜单栏中的Effects项,它将用来调用或取消一些三维绘图功能。清单6-8 程序TRIANGLE相关的代码void RenderScene(void)GLfloat x,y,angle;/Stor
48、age for coordinates and anglesint iPivot = 1;/Used to flag alternating colorsglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);/Turn culling on if flag is setif(bCull)glEnable(GL_CULL_FACE);elseglDisable(GL_CULL_FACE);if(bDepth)glEnable(GL_DEPTH_TEST);elseglDisable(GL_DEPTH_TEST);if(bOutline)glPolygo
49、nMode(GL_BACK,GL_LINE);elseglPolygonMode(GL_BACK,GL_FILL);glRotatef(xRot,1.0f,0.0f,0.0f);glRotatef(yRot,0.0f,1.0f,0.0f);/Begin a triangle fanglBegin(GL_TRIANGLE_FAN);glVertex3f(0.0f,0.0f,175.0f);for(angle=0.0f;angle=(2.0f*GL_PI);angle+=(GL_PI/6.0f) x = 100.0f*(GLfloat)sin(angle);y = 100.0f*(GLfloat)
50、cos(angle);if(iPivot%2)=0)glColor3f(0.0f,1.0f,0.0f);elseglColor3f(1.0f,0.0f,0.0f);iPivot+;glVertex2f(x,y);glEnd();/Begin a new triangle fan to cover the bottomglBegin(GL_TRIANGLE_FAN);glVertex2f(0.0f,0.0f);for(angle=0.0f;angle=(2.0f*GL_PI);angle+=(GL_PI/6.0f) x = 100.0f*(GLfloat)sin(angle);y = 100.0
51、f*(GLfloat)cos(angle);if(iPivot%2)=0)glColor3f(0.0f,1.0f,0.0f);elseglColor3f(1.0f,0.0f,0.0f);iPivot+;glVertex2f(x,y);glEnd();glFlush();设置多边形的颜色到此为止,我们只设置过一次当前颜色,也只画了一个形状。现在有了多边形,就变得有趣多了。我想用多种颜色,这样对我们所做的工作就看得更清楚。颜色实际上是通过顶点来指定的而不是多边形。函数glShadeModel(GL_FLAT)告诉OpenGL用指定的实体颜色来填充多边形。这就是我们可以轻易地改变我们的扇形三角形的颜
52、色的原因。另外,glShadeModel(GL_SMOOTH);告诉OpenGL对每个顶点作光滑消隐。你将会在第八节中学到更多的颜色和消隐的知识。隐藏表面的消隐用方向键来使锥体旋转起来,且不使用菜单Effects中的任何效果。你会发现有点不正常:锥体在正的和负的180度的旋转,且锥体的底面总是面对着你,不是旋转360度。图6-17清晰所示:图6-17 这是因为锥体的底面总是在表面绘制完毕之后才被画出。这也就是说不管锥体朝向哪边,底面总是在最上面。对于一个物体的各个边或部分这种效果不被限制。若有两个物体同时被绘制,且一个在另一个的前面(从观察者的角度),后画的物体仍然可以在前一个物体前面显示出来
53、。你可改变这种怪现象,通过隐藏表面的消隐(Hidden surface removal),且OpenGL中为此给你提供了相应的函数。概念是简单的:当画一个点时,就给它指定了一个Z值,即从观察者的视线来看的距离。然后,当另一个点要画出时,就用它的Z值和原来的点的Z值进行比较。若新的点的Z值比原来的点的Z值大,即它离观察者更近,也就是在原来的点之前,因此原来的点将会被新的点挡住。如果新的点的Z值比原来的点的Z值小,那么它一定是在原来的点的后面,原先的点就不会被挡住了。我们将会第十五节中讨论。要使用深度效果,只需简单调用函数glEnable(GL_DEPTH_TEST);在清单6-8中,当变量bDepth置为TRUE时,就执行深度测试功能了。if(bDepth)glEnable(GL_DEP
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度绿色建筑安全责任协议书
- 二零二五年度公路工程勘察与监理服务合同
- 2025版河砂、碎石道路建设材料采购合同
- 二零二五年度江西离婚法律援助服务合同范本
- 二零二五版企业并购法律服务专项合同范本
- 二零二五年房地产投资合作项目广告推广协议
- 2025年饲用精细化工合成添加剂项目合作计划书
- 2025版租赁中介合同模板:商住两用物业
- 2025版人工智能设备工程分包施工合同
- 二零二五年度拆迁厂房评估与补偿标准执行手册合同
- 妇产科医师晋升副主任医师职称病案分析专题报告四篇汇编
- 边坡工程教学课件汇总完整版电子教案全书整套课件幻灯片(最新)
- DB32∕T 1332-2009 宽体金线蛭养殖技术规程
- 350MW超临界锅炉讲义课件
- --水库除险加固工程下闸蓄水验收建设管理工作报告
- 五十铃、丰田全球化研究
- 沪教版九年级上册化学全册ppt课件
- 智能魔镜项目可行性研究报告【参考模板】
- 印刷包装企业组织机构及职能分配图
- 信息化项目预算明细表
- 有创血压监测的操作流程评分标准
评论
0/150
提交评论