《第三章绘制基本图元.ppt》由会员分享,可在线阅读,更多相关《第三章绘制基本图元.ppt(65页珍藏版)》请在三一办公上搜索。
1、第三章 绘制基本图元,复杂的三维图形是由一个个简单的图元构成的,点和直线段是最简单的几何图元,此外还有多边形、圆,二次曲线、二次曲面、样条曲线、样条曲面等。本章介绍OpenGL的输出图元及底层(设备级)实现算法,以便深层次地掌握图形绘制的原理和方法,提高计算机图形的制作水平。,3.1 坐标系统,计算机只能处理数据,为了能在屏幕上显示三维图形,必须将三维图形与数据相联系,将数据与三维图形联系在一起的唯一纽带,就是坐标。为了描述图形,首先必须建立一个世界坐标系(二维或三维笛卡尔坐标系),在世界坐标系中给出图形对象的位置和方向,然后投影到显示设备的显示区域对应的二维平面上,并扫描转换到帧缓存的整数像
2、素位置。如可以通过两个端点定义一条直线段,通过一组顶点定义一个多边形。对象的坐标范围(x、y、z坐标的最大最小值)称为对象的包围盒,对于二维图形,坐标范围就是包围矩形。,屏幕坐标及图形坐标,显示器上的位置用整型的屏幕坐标来描述,与帧缓存中的像素位置相对应。坐标y值给出像素的行序号,x值给出列序号。屏幕刷新硬件一般从屏幕的左上角开始对像素进行编址,从最上面的0行到最下面的ymax行,对扫描行进行编号;每行从左到右,从0到xmax按列进行编号。图形软件可以按照任意方式设定屏幕位置的图形坐标系统,如可以设定屏幕区域的左下角为原点(视区的默认值),用整数坐标或非整数笛卡尔坐标来描述图形。描述物体对象的
3、坐标值最终转换为帧缓存中的整数像素位置。图元的扫描转换根据给定的坐标来确定要显示的像素位置,如给定一条直线段的两个端点,显示函数必须计算出两端点间位于线段上的所有像素的位置。由于一个像素位置占有屏幕上的一个有限范围,因此图形显示算法必须考虑像素的有限大小,一般假定像素坐标对应于像素区域的中心。,屏幕上的笛卡尔坐标系,显示窗口,上一章的绘图实例中应用gluOrtho2D(xmin,xmax,ymin,ymax)函数设定了一个二维视见空间(二维笛卡尔坐标系),其参数指定了显示窗口的x、y坐标范围。显示窗口的左下角位于坐标(xmin,ymin)处,右上角位于坐标(xmax,ymax)处。如果一个图元
4、的坐标范围完全在显示窗口内,该图元将完整地显示出来,否则,仅仅位于显示窗口范围内的部分图元能显示出来,这涉及到裁剪。,3.2 顶点及图元,在OpenGL中所有的几何物体最终都要描述成一个顶点的有序集合,包括单个点、线段的端点或多边形的顶点。点由其坐标来描述,线是指线段,可由两个端点来描述,多边形是指由线段组成的封闭区域,由各线段的端点来描述。要绘制一个三维点,只需在世界坐标系中指定该点的坐标,OpenGL按指定的大小和颜色来显示这个点,默认的颜色为白色,默认的大小为一个屏幕像素大小,屏幕上绘制的像素是一个正方形区域。多边形常常通过充填其内部的像素来显示,也可以显示其轮廓线。一般而言,多边形可能
5、很复杂,所以OpenGL对多边形做了一些强制限定。首先,多边形的边不能相交,其次多边形必须是凸的,如果指定一个非凸的多边形,将绘出意想不到的结果。对多边形类型作出限制,是为了提高绘制多边形的速度和性能。由于顶点一般是三维的,如果多边形的顶点并不处于同一平面内,经过一系列旋转及改变视点后,这些点将不再组成一个简单的凸多边形,OpenGL无法保证这个多边形得到正确绘制。一般可以通过一系列三角形来逼近多边形,因为三角形的三个顶点肯定位于同一个平面内。,指定顶点坐标,OpenGL中,可以使用函数glVertex()指定顶点的坐标,但顶点既可以是一个纯粹的点,也可以是线段的端点,还可以是多边形的角点,O
6、penGL中共有10种图元,必须明确指明这些顶点构成何种图元。OpenGL中将每组顶点放在函数glBegin()和glEnd()之间,由glBegin()的参数来指定相应的几何图元,然后调用glEnd()来结束这组顶点的定义。,几何图元的参数和含义,绘制三维点,例3-1(POINTS)绘制一系列的三维点,使它们沿Z轴形成一个螺旋圈。glBegin(GL_POINTS);z=-50.0f;for(angle=0.0f;angle=(2.0f*GL_PI)*3.0f;angle+=0.1f)x=50.0f*sin(angle);y=50.0f*cos(angle);/Specify the poi
7、nt and move the Z value up a littleglVertex3f(x,y,z);z+=0.5f;/Done drawing pointsglEnd();,点的属性,点的属性包括颜色和大小,可以调用函数glPointSize()来改变点的大小,该函数有1个参数,指定点的近似直径(以像素为单位),但点的大小设置还是存在限制。另外,点和其它几何图形不同,它不会受到透视的影响,即离视点较远时,看上去并不会变小,离视点更近时,看上去也不会变大。例3-2(POINTSZ):产生一个点逐渐增大的螺旋圈。for(angle=0.0f;angle=(2.0f*3.1415f)*3.0f
8、;angle+=0.1f)/Calculate x and y values on the circlex=50.0f*sin(angle);y=50.0f*cos(angle);/Specify the point size before the primative is specifiedglPointSize(curSize);/Draw the pointglBegin(GL_POINTS);glVertex3f(x,y,z);glEnd();/Bump up the z value and the point sizez+=0.5f;curSize+=step;,3.3 直线段的扫描
9、转换算法,例2-2给出了一个绘直线段的例子,代码如下:glBegin(GL_LINES);/开始定义一组顶点坐标,/每相邻的两个两点组成一对,在该两点之间绘直线glVertex2i(180,15);/定义顶点坐标glVertex2i(10,145);/结束定义一组顶点坐标glEnd();例3-3(LSTRIPS)给出了一个由直线段逼近三维螺旋曲线的例子场景中的直线段由其两端点的坐标来定义,要在光栅显示器上显示一条线段,必须先将两个端点投影到整数屏幕坐标,并确定离两端点间的直线路径最近的像素位置,将颜色值赋予帧缓存中的对应位置。这一过程将一条连续的线段离散为一组整数光栅位置。一般而言,这些位置是
10、实际线段路径的逼近。如计算出的直线位置(10.48,20.51),转换为像素位置为(10,21)。坐标值舍入到整数,使线段呈锯齿状,这就是走样。光栅化特有的锯齿现象在低分辨率系统中特别明显,在高分辨率系统中可以得到改善。平滑光栅线段的有效方法是反走样技术。,直线段的扫描转换算法,直线的扫描转换:根据直线的几何特征,可确定最佳逼近于该直线的一组象素,并且按扫描线顺序,对这些象素进行写操作。两种常用算法:数值微分法(DDA)Bresenham算法。,数字微分法,基本思想:根据线段斜率的不同,在某个坐标轴上以单位间隔对线段采样,在另一个坐标轴上确定最靠近线段的对应整数值。已知过端点P0(x0,y0)
11、,P1(x1,y1)的直线段L的方程为:y=kx+b 直线斜率为 当斜率的绝对值小于1时,从x的左端点x0开始,向x的右端点逐步递增。步长=1(象素单位),计算相应的y坐标y=kx+b;取象素点(x,round(y)作为当前点的坐标。,由直线方程可得下一个采样点的y坐标为:yi+1=kxi+1+b=kxi+b+kx=yi+kx 当x=1时;yi+1=yi+k 即:当x每递增1,y递增k(即直线斜率);注意上述分析的算法仅适用于k 1的情形。在此情况下,x每增加1,y最多增加1。当 k 1时,必须把x,y的地位互换:xi+1=xi+1/b该算法能在有限的线段长度内,根据斜率的不同合理确定尽可能多
12、的像素来逼近该直线段。DDA算法计算像素的位置比直接使用直线方程计算的速度要快,它利用光栅特性消除了直线方程中的乘法,在x或y方向使用合适的增量,在直线路径上逐步得到各像素的位置。但在浮点增量的连续累加中,取整误差的积累使得对于较长线段所得到的像素位置偏离实际线段,且该过程中的取整操作和浮点运算仍然十分耗时。促使人们考虑既能用于直线,又能用于曲线的更通用的扫描线算法。,例:画直线段P0(0,0)-P1(5,2)k=0.4 x int(y+0.5)y+0.5 0 0 0 1 0 0.4+0.5 2 1 0.8+0.5 3 1 1.2+0.5 4 2 1.6+0.5 5 2 2.0+0.5,Bre
13、senham算法,Bresenham画线算法是一种精确而有效的光栅线生成算法,该算法还可用于圆和其它曲线。如下图,首先考虑斜率小于1的直线的扫描转换过程,沿线段路径的像素位置由以单位x间隔的采样来确定。从给定线段的左端点(x0,y0)开始,逐步处理每个后继列(x位置),并在其扫描线y值最接近线段的像素上绘出一点。下图显示了这个过程的第k步。假设已经决定要显示的像素在(xk,yk),那么下一步需要确定在列xk+1=xk+1上绘制那个像素,是在位置(xk+1,yk),还是在(xk+1,yk+1)。,从(xk,yk)像素开始,绘制斜率0m1的直线的屏幕局部,在采样位置xk+1,用dlower和dup
14、per来表示两个像素与直线段路径的垂直偏移。直线段上像素列位置处的y坐标为:y=m(xk+1)+b则:dlower=y yk=m(xk+1)+b yk dupper=(yk+1)y=yk+1 m(xk+1)b要确定两像素中哪一个更接近线段路径,需要测试这两个像素偏移的差:dlower dupper=2m(xk+1)2 yk+2b1,在采样位置xk+1处,像素位置到直线上y坐标之间的垂直距离,设y和x分别为两端点的垂直和水平偏移量,则m=y/x,定义决策参数pk为:pk=x(dlower dupper)=2y xk 2x yk+c 3-1 其中c=2y+x(2b 1),pk的符号与dlower
15、dupper的符号相同(此例中x 0)。假如yk处的像素比yk+1的像素更接近于线段(即dlower dupper),那么参数pk是负的,此时绘制下面的像素,反之,绘制上面的像素。可以利用递增整数运算得到后继的决策参数值。在k+1步,可得决策参数为:pk+1=2y xk+1 2x yk+1+c 3-2将两式相减,可得:pk+1 pk=2y(xk+1 xk)2x(yk+1 yk)但xk+1=xk+1,因而 pk+1=pk+2y 2x(yk+1 yk)其中yk+1 yk 取0或1,取决于参数pk的符号。决策参数的递归运算在线段的坐标端点开始的每个整数x位置进行,起始像素位置(x0,y0)的第一个参
16、数p0由式3-1及m=y/x可得:p0=2y x递推时,仅进行两个常量2y及2y 2x之间的整数加减。,正斜率小于1的线段的Bresenham画线算法的主要步骤概括如下:(1)输入线段的两个端点,并将左端点储存在(x0,y0)中;(2)将(x0,y0)装入帧缓存,画出第一个点;(3)计算常量x、y、2y 和2y 2x,并得到决策参数的第一个值;p0=2y x(4)从k=0开始,在线段路径的每个xk处,进行下列检测:如果pk0,下一个要绘制的点是(xk+1,yk),并且pk+1=pk+2y 否则,下一个要绘制的点是(xk+1,yk+1),并且 pk+1=pk+2y 2x(5)重复步骤4,共x 1
17、次。当 斜率 1时,也必须把x,y的地位互换。例:会直一条线段,端点为(20,10)和(30,18)。该线段的斜率为0.8,且x=10,y=8初始决策参数为:p0=2y x=6后继决策参数的增量为:2y=16,2y 2x=-4绘制初始点(x0,y0)=(20,10),并根据决策参数确定后即像素点的位置为:,沿端点(20,10)和(30,18)用Bresenham画线算法绘制的像素点,线的属性,直线段的属性包括颜色、线宽和线型。OpenGL中,线的颜色用图元的颜色函数设定,而线宽和线型则用单独的线函数选择。例3-4(LINESW):用glLineWidth()函数设置线宽,以不同的宽度绘直线。还
18、可以用点线或虚线模式绘直线,称为点画线glEnable(GL_LINE_STIPPLE)启用点画线功能glLineStipple(GLint factor,GLushort pattern)创建点画模式。pattern是一个16位的值,指定了一个模式,它的每一位都表示线段的一部分,处于打开或关闭状态,缺省情况下,每位对应一个像素。factor作为一个乘法因子,用于增加模式的宽度。例3-5(LSTIPPLE):显示加宽的点画线,3.4 圆的生成算法,圆的方程为:(x-xc)2+(y-yc)2=r2 3.4.1利用这个方程,可以沿x轴从xc-r到xc+r以单位步长计算对应的y值,从而得到圆周上每点
19、的位置:y=yc(r2-(xc-x)2)1/2 3.4.2该方法的每一步包含很大的计算量,而且所绘制像素的间距不一致,如下图所示。我们可以在圆斜率的绝对值大于1后,交换x和y(即步进y值并计算x值)来调整间距,但增加了计算量和处理过程。总之,该方法不是一种理想的画圆方法。,用方程3.4.2绘制的正半圆,另一种消除不等间距的方法是使用极坐标r和来计算圆周上的点,圆的极坐标方程为:x=xc+rcos y=yc+rsin 使用上述方法以固定角度为步长可以生成等间距得点来绘制圆,尽管利用极坐标能生成等距点,但三角函数的计算十分耗时。可以利用圆的对称性减少计算量,圆的形状在每个象限中是相似的,如果确定了
20、圆在第一象限中的位置,则可以生成圆在第二象限的部分,因为圆相对于y轴是对成的。根据圆相对于x轴的对称性,由第一象限和第二象限中的圆弧,可以得到第三象限和第四象限中的圆弧。在八分圆之间也有对称性,一个象限中相邻八分圆的圆弧对于45直线是对称的,如下图。图中八分之一圆上的一点(x,y)可映射到其它7个八分圆的点。,利用圆的对称性,可由1个八分圆上的点,映射到其余7个八分圆上的点,利用圆的对称性,仅需计算从x=0到x=y这段圆弧的点就可得到整个圆的所有像素位置。在该八分圆中,圆弧斜率的绝对值1,x=0时的斜率为0,x=y时圆弧的斜率为-1.0。使用对称性及圆的方程来确定圆周上的像素位置,仍然需要大量
21、的计算,更有效地画圆算法是采用类似Bresenham画线算法,通过设定在每一步采样步骤中寻找最接近圆周像素的决策参数。但圆的方程是非线性的,包含平方运算,一个避免平方运算的方法是:检验两像素间的中点位置是在圆边界之内还是之外。中点画圆算法:对于中心为(xc,yc)、半径为r的圆,先计算圆心在(0,0)的圆的像素位置,然后通过将xc加到x且yc加到y,将圆平移到真正位置。在第一象限,圆弧段从x=0到x=y,曲线的斜率从0变到-1.0。可以在该八分圆上的正x方向取单位步长,并使用决策参数来确定每一步两个可能的y位置中,哪一个更接近于圆的位置,其余7个八分圆中的位置可由对称性得到。定义圆函数:f(x
22、,y)=x2+y2-r2任何点的相对位置可由圆函数的符号来决定:,0,如果(x,y)位于圆外圆函数即为决策参数,下图给出了采样位置xk+1上两个候选像素间的中点。,圆周上采样位置xk+1上候选像素间的中点,假设刚在(xk,yk)绘制了一个像素,下一步需要确定是像素(xk+1,yk)还是像素(xk+1,yk-1)更接近于圆,在两像素的中点求决策参数的值:pk=f(xk+1,yk-1/2)=(xk+1)2+(yk-1/2)2-r2假若pk 0,则该中点位于圆内,扫描线yk上的像素接近于圆周边界。否则该中点位于圆外或圆周上,选择扫描线yk-1的像素。,后续的决策参数可以使用增量运算得到,如对采样位置
23、 xk+1=xk+2处的圆函数求值,可以得到下一个决策参数的循环表达式:pk+1=f(xk+1+1,yk+1-1/2)=(xk+1)+12+(yk+1-1/2)2-r2=pk+2(xk+1)+(y2k+1-y2k)-(yk+1-yk)+1其中,yk+1是yk或yk-1,取决于pk的符号。若pk为负,则yk+1=yk,pk+1的增量为2xk+1+1;若pk为正,则yk+1=yk-1,pk+1的增量为2xk+1+1-2yk+12xk+1和2yk+1的求值也可以通过增量的方式求得:2xk+1=2xk+2 2yk+1=2yk-2在起始位置(0,r)处,这两项的初始值分别为0和2r。在起始位置(0,r)
24、处,初始决策参数为:p0=f(1,r-1/2)=1+(r-1/2)2-r2=5/4-r若以整数屏幕坐标为参数,则中点方法使用整数加减来计算圆周的像素位置。,中点画圆算法的步骤:(1)输入圆半径r和圆心(xc,yc),并得到圆周(圆心在原点)上的第一个点:(x0,y0)=(0,r)(2)计算决策参数的初始值:p0=5/4-r(3)在每个xk位置,从k=0开始,计算决策参数,若pk0,则圆周下一像素位置为(xk+1,yk),并且 pk+1=pk+2xk+1+1否则圆的下一点为(xk+1,yk-1),并且 pk+1=pk+2xk+1+1-2yk+1其中,2xk+1=2xk+2且2yk+1=2yk-2
25、(4)确定其它7个八分圆中的对称点。(5)将每个计算出来的像素位置(x,y)平移到圆心在(xc,yc)的圆周上,并绘出各点(x+xc,y+yc)。(6)重复步骤3到步骤5,直至xy。,例,使用中点画圆算法画圆给定圆半径r=10,确定在第一象限从x=0到x=y沿八分圆的各像素点位置。决策参数的初始值为:p0=1-r=-9对于中心在原点的圆,初始点(x0,y0)=(0,10),决策参数的初始增量值:2x0=0,2y0=20使用中点画圆法计算的后续决策参数值及像素位置如下:,用中点画圆法得到的像素位置(空心圆为第一象限的对称位置),使用折线来逼近曲线,常见的曲线除圆以外,还有椭圆、圆锥曲线、三角和指
26、数函数、样条曲线等。生成圆和椭圆等基本曲线的函数并未作为图元包含在OpenGL的核心库中,但核心库中包含了Bezier样条曲线,该曲线是由一组离散点定义的多项式。显示曲线的简单方法是使用直线段来逼近曲线,仅需确定一组曲线上的点并将它们连接成一组直线段。折线中的线段越多,曲线越平滑。,3.5 填充区图元,图形学中常常需要用一种颜色或某种图案来填充一个区域,尽管有可能使用各种形状,但图形库一般不支持任意的填充形状,多数库函数要求填充区为多边形。多边形是由三个或更多的顶点连接而成的封闭图形,由于多边形的边界为直线段,因而比其他形状更容易处理。可将曲线转换为由分段折线构成的多边形,曲面也可以用一系列多
27、边形面片来逼近。如果一个多边形的所有内角均小于180,则该多边形为凸(convex)多边形。等价定义:它的内部完全在它的任一边及其延长线的一侧。如果任意两点位于凸多边形的内部,则其连线上的任一点也在凸多边形的内部。不是凸多边形的多边形称为凹(concave)多边形。退化多边形是指有共线或重合顶点的多边形,共线的顶点生成一条线段,重叠的顶点可以生成多余线段、重叠边或长度为0的边。为了软件的鲁棒性,图形软件可以拒绝退化或非平面的多边形。凹多边形也会产生一些问题,对凹多边形的填充及其它处理比较复杂,常常将凹多边形分割成一组凸多边形以提高效率。,可以为多边形的每一条边建立一个向量,用相邻边的叉积来测试
28、凸凹性,凸多边形的所有向量叉积均同号,如果某些叉积为正而另一些为负,则为凹多边形。识别凹多边形的另一个方法是观察多边形顶点位置与每条边延长线的关系。如果有些顶点在某一延长线的一侧而有些顶点在另一侧,则为凹多边形。多边形的填充算法分为扫描线填充算法和种子填充算法。扫描线的含义来源于光栅显示器,对屏幕上的每个像素点,按从上到下,从左到右的顺序进行显示,水平方向从左到右每扫描一行,就是一条y方向的扫描线,同理,从上到下也能形成x方向的扫描线。扫描线填充算法的原理是:待填充区域按y方向(或x方向)扫描线的顺序,计算扫描线与多边形的相交区间,再用指定的颜色填充这些区间的像素,区间的端点可以通过计算扫描线
29、与多边形边界的交点求得。,通用多边形的扫描线填充算法,下图给出了多边形区域实心填充的扫描线过程。对每一条与多边形相交的扫描线,与边的交点从左到右排序,且将每一对交点之间的像素位置包括这对像素在内,设定为指定颜色,本例中与边界的四个交点定义了两段内部像素:从x=10到14的5个像素,以及从x=18到x=24的7个像素。如果是用图案填充多边形,则沿一条扫描线的每一个像素颜色由与图案重叠的位置确定。,穿过多边形区域的扫描线上的内部像素,有时情况比较复杂,当一条扫描线经过多边形的一个顶点时,扫描线在该顶点处与多边形的两条边相交,这种情况可能导致在这条扫描线上的交点要增加两个点。下图给出了在顶点处与多边
30、形相交的两条扫描线。,扫描线y与偶数条边相交,必须将该顶点处的交点作为2个交点,才能正确标识该扫描线上的两对交点之间为内部像素段。扫描线y与多边形的5条边相交,必须将该顶点除的交点作为1个交点,才能正确标识该扫描线上的两对交点之间为内部像素段。通过观察相交边相对于扫描线的位置,可以发现扫描线y和扫描线y间的拓扑差异。对于扫描线y,共享一个顶点的两条相交边位于扫描线的两侧,对于扫描线y,两条相交边位于扫描线的同一侧。,扫描线与多边形顶点相交,y生成奇数个交点,y生成偶数个交点,可以通过顺时针或逆时针方向来搜索多边形边界,并观察从一条边移动到另一条边时顶点y坐标的相对变化来判别这个点。假如两条相邻
31、边的三个端点y坐标值单调递增或递减,那么对于任何穿过该顶点的扫描线,必须将该相交顶点(中间的共享顶点)计为1个交点。否则,该相交顶点(中间的共享顶点)即为多边形边界上的一个局部极值,(极大或极小),须将该相交顶点计为2个交点凸多边形的扫描线填充对于凸多边形,扫描线填充过程可以简化,每一条扫描线与多边形至多有两个交点,可以使用坐标范围来确定哪些边与扫描线相交,当扫描线与单个顶点相交时,仅绘出该点本身,当有两个交点时,两交点之间的部分都是多边形内部的点。,种子填充算法,区域填充的另一种方法是从区域的一个内部点开始,由内向外逐点绘制直到边界。可以使用单一颜色或一个图案来填充一个区域。假如边界是以单一
32、颜色指定的,则填充算法可逐个像素地向外处理,直至遇到边界颜色。该算法从一个内点开始检测相邻位置的颜色,若检测位置不是该边界颜色,就将它设置成边界颜色,并再检测其相邻位置,这个过程持续到检测完区域边界颜色范围内的所有像素为止。区域填充算法要求区域是连通的。从一个当前位置检测其相邻像素有两种方法:4-连通和8-连通。4-连通即检测当前位置的上、下、左、右4个方向的相邻像素,8-连通即检测当前位置的上、下、左、右、左上、右上、左下及右下8个方向的相邻像素。,4-连通 8-连通,4-连通区域 8-连通区域,图案填充,可以将一个区域填充为单一颜色,也可以用某种图案进行填充,也可以不填充而只绘制其边界。对
33、多边形可以使用不同颜色、线宽和线型绘制其边框,还可以为区域的前向面和后向面设置不同的显示属性。填充图案可以用一个为不同位置指定不同颜色的矩形颜色阵列来给出,即一个填充图案可当作一个点阵来指定,其中每一位置指定一种颜色。描述填充图案的矩阵是一个应用于显示区域的掩模。有些图形系统可以设置掩模的初始位置,从该初始位置开始,掩模在水平方向和垂直方向反复填充直到整个区域都填满无重叠的掩模。这种用矩形图案填充一个区域的处理称为平铺,而矩形填充图案有时也称为平铺图案。填充图案的起始位置可设定在填充区内的任意位置,有些软件包将显示窗口的坐标原点作为图案的起始位置。,用OpenGL绘制填充区,绘线图元时,用GL
34、_LINE_LOOP参数可以绘制封闭的多边形,得到的是线框图,还可以用颜色对多边形进行填充。最简单的多边形是三角形,下面的代码绘制两个三角形,并用当前颜色进行填充,如下图。glBegin(GL_TRIANGLES)glVertex2f(0.0f,0.0f);/V0 glVertex2f(25.0f,25.0f);/V1 glVertex2f(50.0f,0.0f);/V2 glVertex2f(-50.0f,0.0f);/V3 glVertex2f(-75.0f,50.0f);/V4 glVertex2f(-25.0f,0.0f);/V5glEnd;,两个使用GL_TRIANGLES绘制的三角
35、形,三角形的环绕方向,上图中标出了连接三角形顶点的边线的箭头,第一个三角形边线的走向依次从V0到V1,然后再到V2,最后回到V0,这个路径是由顶点的指定次序决定的,呈顺时针方向排列,第二个三角形也呈顺时针排列。顶点的指定次序以及排列方向称为环绕(winding)。上图中的三角形具有顺时针方向的环绕,如果将左边的三角形交换V4和V5的位置,就得到一个逆时针方向的环绕,如下图。,两个具有不同环绕的三角形,缺省情况下,OpenGL认为逆时针的环绕方向是多边形的正面,因此上图左侧的三角形是这个三角形的正面,右侧的三角形是那个三角形的背面。一个多边形有两个面,正面和背面,可以给正面和背面设定不同的颜色及
36、其它属性。可以调用函数 glFlortFace(GL_CW)指定多边形的正面和背面,参数GL_CW定义顺时针环绕的多边形为正面,参数GL_CCW定义逆时针环绕的多边形为正面。三角条带:对于许多表面和形状,有时需要使用GL_TRIANGLE_STRIP图元绘制一串相连的三角形。下图显示了一组由5个顶点构成的3个三角形的绘制过程,可以看出,这些顶点并不是按照他们的指定顺序遍历的,原因是为了保持每个三角形的环绕方向一致。它的连接次序是V0、V1、V2,接着是V2、V1、V3,然后是V2、V3、V4,依此类推。,三角条带的环绕方向,三角扇形:还可以使用参数GL_TRIANGLE_FAN来创建一组围绕一
37、个中心点的相连三角形,称为三角扇形。下图为由4个顶点所产生的包括3个三角形的三角扇形。在指定前3个顶点表示第一个三角形之后,后续的顶点和原点(V0)以及前面最近邻的那个顶点(Vn-1)组成下一个三角形。,有4个顶点构成的三角扇形,创建实心物体,例3-6(TRIANGLE)使用两个三角扇形创建一个实心的圆锥,第一个三角扇形围成这个圆锥的侧面,将圆锥的顶点作为第一个顶点,剩下的顶点沿z轴向下分布在圆锥底面的圆周上。第二个三角扇形围成圆锥的底面,是一个圆,位于xy平面上。通过对三角形交替地着以绿色和红色,可以清楚地显示各个三角形。创建圆锥侧面的代码如下:glBegin(GL_TRIANGLE_FAN
38、);glVertex3f(0.0f,0.0f,75.0f);for(angle=0.0f;angle(2.0f*GL_PI);angle+=(GL_PI/8.0f)/Calculate x and y position of the next vertex x=50.0f*sin(angle);y=50.0f*cos(angle);/Alternate color between red and green if(iPivot%2)=0)glColor3f(0.0f,1.0f,0.0f);else glColor3f(1.0f,0.0f,0.0f);/Increment pivot to ch
39、ange color next time iPivot+;/Specify the next vertex for the triangle fan glVertex2f(x,y);glEnd();,创建圆锥底面的代码如下glBegin(GL_TRIANGLE_FAN);/Center of fan is at the origin glVertex2f(0.0f,0.0f);for(angle=0.0f;angle(2.0f*GL_PI);angle+=(GL_PI/8.0f)/Calculate x and y position of the next vertex x=50.0f*sin
40、(angle);y=50.0f*cos(angle);/Alternate color between red and green if(iPivot%2)=0)glColor3f(0.0f,1.0f,0.0f);elseglColor3f(1.0f,0.0f,0.0f);/Increment pivot to change color next time iPivot+;/Specify the next vertex for the triangle fan glVertex2f(x,y);/Done drawing the fan that covers the bottomglEnd(
41、);可用右键弹出菜单,设置多边形的颜色,上例中我们给多边形设置了不同的颜色,颜色实际上是以顶点为单位指定的。而不是以多边形为单位,最后一个顶点的颜色作为整个多边形的填充颜色。代码:glShadeModel(GL_FLAT);指定用当前颜色填充这个多边形,代码:if(iPivot%2)=0)glColor3f(0.0f,1.0f,0.0f);elseglColor3f(1.0f,0.0f,0.0f);在指定下一个顶点前将当前颜色修改为红色或绿色。,3.6 字符图元,图形显示中常包括文字信息,包括字母、数字或其他字符等。字体是指一组按照特定尺寸和格式的模板字符样式。字体有两种不同的表示方法,第一种
42、称为位图字体(或光栅字体),每个字符都由一个二值形式的点阵图案表示,如图3.6a,由于所有字符都以完成扫描转换,绘制一个字符只需将该字符的位图直接映射或复制到帧缓存的指定位置,值为1的位对应的像素位置将用当前颜色进行显示。尽管可以由一组点阵字符生成不同尺寸以及类似粗体、斜体等其它字体,但效果并不好。第二种表示方法称为轮廓字体,又称笔画字体或向量字体。每个字符的外形轮廓均由直线和曲线段来描述,如图3.6b,字符轮廓的内部按填充区进行扫描转换处理。与位图字符相比,轮廓字体在进行放大时其字符形状不会变形。,OpenGL字符函数,OpenGL基本库仅为显示单个字符和文字串提供基本支持。GLUT中预定义
43、了一些字库,可以显示位图和轮廓字体。调用函数glutBitmapCharacter(font,character)以当前光栅位置作为左下角显示GLUT位图字符。参数font用GLUT符号常量赋值,用来指定一特定字符集,参数character赋以ASCII码或其它要显示的字符。调用函数glutStrokeCharacter(font,character)显示GLUT轮廓字符,3.7 反走样,在图形光栅化的过程中由于图元的坐标点离散化为整数像素位置,因此光栅化后生成的图元具有锯齿形或阶梯状的外观。当我们把对象的连续表示(无限的分辨率)变换成对象的离散表示时,不可避免地会出现此类误差,这种由于低频采
44、样(不充分采样)而造成的图形失真称为走样(aliasing)。另一个不太引人注目的走样现象是不同走向的线段具有不同的亮度,虽然所有线段都用同样的颜色亮度来显示,但一条斜线看上去要比一条水平或垂直线暗一些。这一现象的原因可由图3.7.1解释,图中水平线上的像素间的距离为一个单位长度,而斜线上的像素间的距离大约为1.414个单位长度,密度上的带来亮度感觉上的差异。如果一个物体没有与像素栅格对齐,或者不能与像素栅格吻合,会出现篱栅问题。图3.7.2(a)表示一道桩与桩之间的距离并非像素间距整数倍的篱栅。将该篱栅扫描转换后桩的端点都要向像素位置靠拢对齐,结果是桩之间的距离不再均匀相等图3.7.2(b)
45、。此现象有时称为局部走样,这时篱栅的总长度基本上是正确的。若要保持桩距均匀,篱栅的总长度就会明显失真图3.7.2(c),这一现象有时称为总体走样。,反走样,尽管数学上的线段只有长度没有宽度,但光栅化后的线段为了可见性,必须有一个宽度,帧缓存以像素为最小单位,若一条理想的线段在帧缓存里的宽度为一个像素,当仔细观察理想的一个像素宽的直线时,会发现该线段只是部分覆盖了1像素大小的方形区域,如图3.7.3。,反走样,提高显示器的分辨率只能减轻而不能消除锯齿问题。,反走样,对于能显示两级以上亮度的光栅系统,可以采用反走样方法适当地修改沿图元边界的像素亮度,平滑边界,减小锯齿状、斑块状的亮度差异,改善显示
46、效果。一种简单、直接的反走样方法,就是把屏幕看成由比实际更细的网格所覆盖,从而增加采样频率,然后根据这种更细网格,使用采样点来确定每个屏幕像素的合适亮度。这种在高分辨率下对对象进行采样并在较低分辨率上显示其结果的技术称为过采样(supersampling),也称为后滤波(postfiltering)。另一种方法是通过计算待显示的每个像素在对象上的覆盖区域来确定像素的亮度。计算覆盖区域的反走样称为区域采样(area sampling),也称为前滤波(prefiltering)。还可以通过移动像素区域的显示位置而实现反走样,这种技术称为像素移相(pixel phasing)。,前滤波与后滤波,滤波
47、的概念来源于信号处理领域。若将亮度作为信号,则颜色均匀的、亮度缓慢变化的区域对应于低频部分,具有边界清晰的、亮度快速变化的区域对应于高频部分。为了减少图像空间里的线段与其它线段的参差不齐现象,希望将亮度的突然变化“抹平”,从信号处理角度来看,就是将高频率的成分“过滤掉”。前滤波就是在采样前对原始连续信号进行处理直接获取各个像素应有的亮度(在采样之前进行滤波)。后滤波则首先从原始连续信号获取离散采样值,然后再用这些采样值计算像素值(在采样之后进行滤波)。,区域采样,区域采样属于前滤波技术,把一个栅格网叠放在需要栅格化的物体上,对于每一个与该物体相叠交的像素区域,计算该区域与物体叠交的百分比。该百
48、分比决定了该像素的总体颜色中有多少成分来源于该物体,该像素区域与物体叠交的百分比越高,该物体对像素总体颜色的影响就越大。图3.7.4a中用宽度为1个像素的长方形区域来代表一条直线,该长方形区域与每个与其叠交的像素的面积百分比通过解析方式算出。如果背景为黑色,直线为白色,计算所得的百分比可直接用来设定各像素的颜色(图3.7.4b)。,虽然图3.7.4b所示经过区域采样得到的直线的离散逼近看起来有些“模糊”,但它不再有从物体颜色到背景颜色之间的突然过渡。而这种突然过渡是使用普通扫描转换的结果(图3.7.4c)。在减少图像失真与造成图像模糊两者之间力求折中是已滤波为基础的反走样技术的特征。,直线段的
49、过采样,直线段的过采样可以用多种方式来完成。对于直线段,可以把每个像素分成一定数目的子像素,并统计沿直线路径的子像素数目,再将每个像素的亮度设置为正比于子像素数目的值。图3.7.5中,每个正方形像素区域被分成9个大小相等的正方形子像素,阴影区域表示由Bresenham算法选择的子像素。由于任何像素中可供选择的子像素的最大数目为3,因次该方法能提供0以上的3种亮度等级。该例中位置(10,20)像素的亮度设置为3;位置(11,21)和(12,21)像素的亮度设置为2;位置(11,20)和(12,22)像素的亮度设置为1。因此,该直线分布在较大数目的像素上,并且通过在阶梯状附近显示有些模糊的直线路径
50、,从而平滑阶梯状效果。如果要使用更多的亮度等级来实现直线段的反走样,就需要增加每个像素中的子像素采样数目,如16个子像素能给出0以上的4个亮度等级,25个子像素能给出5个等级。对于宽度超过1个像素的直线段的反走样,也可以采用该方法处理,如图3.7.6。,由于首先获得离散的采样值,然后再由这些采样值计算像素值,因此过采样是一种后滤波技术。但由于这只不过是用每个像素范围内有限的采样值来求准确的解析解的近似值,过采样技术也可认为是区域采样法的一种近似实现。,低通滤波,低通滤波是一种给每个像素计算出一个新值的后滤波技术。这个新值是该像素的原始值及其相邻若干像素的加权平均。计算所需的权值存放在一个(2n