像素(pixel或pel,是picture element的简写):每个屏幕点称为一个像素。像素信息从应用程序转换并放入帧缓冲区的过程称之为扫描转换过程。
走样:光栅扫描的图形显示器是画点设备,绘制图形时只能用尽可能靠近图形的像素点集来近似表示图形,因而会产生失真。这种失真称为走样。
程序2-1 OpenGL绘图矩形的简单例子 #include <gl/glut.h> void Initial(void) { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //设置窗口背景颜色为白色 glMatrixMode(GL_PROJECTION); //设置投影参数 gluOrtho2D(0.0,200.0,0.0,150.0); } void Display(void) { glClear(GL_COLOR_BUFFER_BIT); //用当前背景色填充窗口 glColor3f(1.0f, 0.0f, 0.0f); //设置当前的绘图颜色为红色 glRectf(50.0f, 100.0f, 150.0f, 50.0f); //绘制一个矩形 glFlush(); //处理所有的OpenGL程序 } int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //初始化窗口的显示模式 glutInitWindowSize(400,300); //设置窗口的尺寸 glutInitWindowPosition(100,120); //设置窗口的位置 glutCreateWindow("矩形"); //创建一个名为矩形的窗口 glutDisplayFunc(Display); //设置当前窗口的显示回调函数 Initial(); //完成窗口初始化 glutMainLoop(); //启动主GLUT事件处理循环 return 0; }
绘制流水线的基本结构从概念上包括三个阶段:应用程序阶段、几何阶段和光栅阶段,如图2-32所示。而这三个阶段本身也可以是一条流水线,可以将其划分为若干个功能阶段。功能阶段规定了该阶段所要执行的任务,而不限制任务在流水线中的执行方式。
应用程序阶段的主要任务是接受各种输入设备的输入,完成相应的操作,并将生成的所用图形对象送至几何阶段。几何阶段主要实现大部分的多边形和顶点操作,它可以分为模型与视点变换、光照、投影、裁剪和屏幕映射等五个功能阶段。几何阶段完成之后,就进入光栅阶段,它实现图形对象的扫描转换,即根据几何阶段输出的顶点、颜色和纹理坐标,计算出屏幕上每个像素的颜色属性并存入帧缓冲存储器,称为颜色缓冲存储器中。光栅阶段还实现可见性判断,即在颜色缓冲存储器中保留可见的图形对象。
透明处理:
最简单的方法是先处理不透明物体以决定可见不透明表面的深度,然后将透明物体的深度值与先前存在的帧缓冲器中不透明面的深度值进行比较,将可见透明面的光强与其后面的可见不透明面的光强综合考虑而进行绘制。
阴影处理
阴影是由于物体截断了光线而产生的,所以如果光源位于物体一侧的话,阴影总是位于物体的另一侧,也就是与光源相反的一侧。可以将产生具有阴影的图形绘制算法分为两个基本步骤:
(1)将视点移到光源位置,用多边形区域排序消隐算法,将多边形分成两大类:向光多边形和背光多边形。所谓向光多边形是指那些从光源看过去是可见的多边形;而背光多边形是指那些从光源看过去是不可见的多边形,包括被其它面遮挡了的多边形和反向面多边形。向光多边形不在阴影区内,背光多边形在阴影区内。
(2)将视点移到原来的观察位置,对向光多边形和背光多边形进行消隐,并选用一种光照模型计算多边形的亮度,就可得到有阴影效果的图形。
图形显示技术中有三种分辨率:
(1)屏幕分辨率:也称光栅分辨率或物理分辨率,它决定了显示系统最大可能的分辨率,通常用水平方向上的光点数与垂直方向上的光点数的乘积来表示。
(2)显示分辨率:是计算机显示控制器所能够提供的显示模式分辨率,实际应用中简称为显示模式。对于文本显示方式,显示分辨率用水平和垂直方向上所能显示的字符总数的乘积表示;对于图形显示方式,则用水平和垂直方向上所能显示的像素点总数的乘积表示。
(3)图形的存储分辨率:是指帧缓冲区的大小,一般用缓冲区的字节数表示。由于帧缓冲存储器的大小不仅与显示分辨率有关,还与像素点的颜色数有关,所以帧缓存大小的计算为:
其中:x、y分别为表示当前显示分辨率下x、y方向上的像素点总数,n为颜色数或灰度等级数。
(1) 组合像素法(Packed Pixel Method)
在组合像素法中,一个图形像素点的全部信息被编码成一个数据字节,按照一定方式存储到帧缓存中,编码字节的长度与点的属性(如颜色、灰度)有关。
图2-38 组合像素法
(2)颜色位面法(Color Plane Method)
在颜色位面法中,帧缓存被分成若干独立的存储区域,每一个区域称为一个位面(Bit Plane),如下图所示,每个位面控制一种颜色或者灰度,每一个图形像素点在每个位面中占一位,通过几个位面中的同一位组合成一个像素。
DDA画线
【例】用Bresenham画线法在起点A(0,0)和终点B(5,2)之间生成一段直线。
�(1) 初始误差:d0=? ,k=?
(2) x每增加1,y的值相应 递增直线的斜率值k,即di+1=di+k
d1=? (< , >=) 0.5 取(0+1,0)=(1,0)或(1,1)?
d2=? (< , >=) 0.5 取?
d3=?
。。。
(1) 初始误差:d0=0,k=2/5=0.4
(2) x每增加1,y的值相应 递增直线的斜率值k,即di+1=di+k
d1=0+0.4=0.4<0.5 取(0+1,0)=(1,0)
d2=0.4+0.4=0.8>0.5 取(2,1)
d3=0.8+0.4=1.2 因di+1≥1,所以d3-1 =0.2<0.5 取(3,1)
d4=0.2+0.4=0.6>0.5 取(4,2)
d5=0.6+0.4=1.0 因di+1≥1,所以d5-1 =0.0<0.5 取(5,2)
(0,0),(1,0),(2,1),(3,1),(4,2),(5,2)
画圆
坐标系
常用的坐标系如下图所示。
世界坐标系:用于图形场景中的所有图形对象的空间定位、观察者(视点)的位置和视线的定义等等。
局部坐标系:为了几何造型和观察物体方便起见,独立于世界坐标系定义的二维或三维笛卡儿坐标系称为局部坐标系。在局部坐标系中定义的"局部"物体,通过指定局部坐标系在世界坐标系中的方位,利用几何变换,就可以将"局部"定义的物体变换到世界坐标系内,使之升级成为世界坐标系中的物体。
观察坐标系:
观察坐标系通常是以视点的位置为原点,通过用户指定的一个向上的观察向量来定义的一个坐标系,缺省为左手坐标系。观察坐标系主要用于从观察者的角度对整个世界坐标系内的图形对象进行观察,以便简化几何物体在视平面(又成为成像面或投影面)的成像的数学演算。
图4-4 坐标系的分类
1.建模坐标系:又称造型坐标系,用来定义基本图素或图段。
2.用户坐标系:也称世界坐标系用于定义用户整图或最高层图形结构。
3.观察坐标系:主要用于指定裁减空间,确定形体的哪一部分要显示输出或通过定义观察平面,把三维形体的用户坐标变换成规格化的设备坐标。
4.规格化设备坐标系:用来定义视图区,其取值范围一般约定为(0.0, 0.0, 0.0)到(1.0, 1.0, 1.0)。
5.设备坐标系:设备坐标系是图形输入输出设备的坐标系。
三维观察流程:
图元、图素\\体素
Bresenham直线算法生成的直线图形一般都呈阶梯状(见下图),实际上,这是光栅图形的一种走样现象。这种走样现象是由于采用离散量表示连续量引起的。通常,我们把由离散量表示连续量引起的失真称为走样;把减少或克服走样效果的技术称为反走样技术,简称反走样。几何元素
光栅图形的走样有如下几种:
a) 产生阶梯或锯齿形;
b) 细节或纹理绘制失真; 为了避免走样,采样频率至少应是信号最高频率的2倍
c) 狭小图形遗失;
d) 实时动画忽隐忽现、闪烁跳跃。
光栅图形的反走样方法主要有两类:
第一类是超采样或称后置滤波。这类算法的基本思想着眼于提高分辨率即增加采样点。
第二类方法称为前置滤波。即:把像素作为一个有限区域而不是一个面积为零的点来处理,从而对区域采样。
简单区域采样
当在一个多灰度级的显示器上显示一条黑线,若一个象素落在线条上,则将它置成黑色;若一个象素与线条部分相交,根据相交部分的大小来选择不同的灰度。相交部分大的象素更黑一些,相交部分小的象素更白一些,这种方法将产生模糊的边界,以此来减轻锯齿效应。
对称(反射)是产生对象的镜像的一种变换。
1.点:点是0维几何分量,包括端点、交点、切点和孤立点等。
2.线:线是一维几何元素,是两个或多个邻面的交界。
3.面:面是二维几何元素,是形体上一个有限、非零的区域,由一个外环和若干个内环界定其范围。
4.环:环是有序、有向边(直线段或曲线段)组成的面的封闭边界。环中的边不能相交,相邻两条边共享一个端点。确定面的最大外边界的环称之为外环;确定面中内孔或凸台边界的环称之为内环。通常,外环的边按逆时针方向排序,而内环的边按顺时针方向排序,这样在面上沿一个环前进,其左侧总是面内,右侧总是面外。
5.体:体是三维几何元素,由封闭表面围成空间,也是欧氏空间R3中非空、有界的封闭子集,其边界是有限面
实体的定义
由于数学中的点、线、面是其所代表的真实世界中的对象的一种抽象,他们之间存在着一定的差距
- 4.2 三维形体的表示
1、线框模型
2、实体模型(实体造型技术) CSG
可以将实体模型的表示大致分为三类:
(1)边界表示(Boundary representation, B-reps):用一组曲面(含平面)来描述物体。
(2)构造实体几何表示:将实体表示成基本体素的组合,采用并、交、差等运算构造新的形体。
(3)空间分割(Space-partitioning)表示:将包含一物体的空间区域划分成一组小的非重叠的连续实体(通常是立方体)。
构造实体几何法(CSG,Constructive Solid Geometry)由两个实体间的并、交或差操作生成新的实体。
八叉树
八叉树(octrees)又称为分层树结构,它对空间进行自适应划分,采用具有层次结构的八叉树来表示实体。
1. 四叉树
把平面图形对象用一个矩形包围起来,如下图所示。这个矩形就是四叉树的根节点,它可能处于三种状态:完全被图形覆盖F,部分被覆盖B或完全没有被覆盖E。当节点处于状态F或E,则四叉树建立完毕, 否则将其等分为4个区域(象限),分别标以1,2,3,4,它们构成一 层子节点。 继续考察这四个象限的状态,直至给定精度下不出现B型象限为止。
图4-20 二维平面图形对象的四叉树表示
2.八叉树
八叉树方法与四叉树类似,用一个空间的长方体来包围一个三维实体,每次把它分为8个卦限来进行判断。
图4-21 三维空间分成八个卦限及其节点表示
BSP树
二叉空间分割(binary space partitioning,BSP)方法每次将一实体用任一位置和任一方向的平面分为二部分。这种方法可将分割平面的位置和方向按适合于实体的空间属性来确定,更为有效。
- 层次结构:
http://wenku.baidu.com/view/3ffbd8ea172ded630b1cb63b.html
基于分数维理论的随机模型:由A.Fournier等人基于分数维理论提出来的一种过程式模拟,它能够有效地模拟海岸线和山脉等自然景物。
基于文法的模型 :由A.R.Smith用正规文法来构造植物等结构性较强的自然景物,是一个并行重写系统,由一组产生式规则组成。
粒子系统模型:由随着时间变化的一组粒子组成的,此模型可以用来模拟火、烟、雾等,也可以用来模拟被风吹动的草和灌木等。
不同的段和基本图形元素在各自的建模坐标系中定义。当每个图段放进图段树中时,将段和基本图形元素放在用户(世界)坐标系中建立引用,引用的位置、图素和段的大小、方向等各不相同。
建模坐标系到用户坐标系的坐标变换,该变换称为建模变换(Modeling Transformations)。典型的建模变换包括平移、旋转和缩放,在某些应用中还包括其他变换。
OpenGL支持创建多级显示列表,即在glNewList和glEndLsit函数对之间允许调用glCallList函数来执行其他显示列表。通过显示列表的嵌套可以实现复杂物体的层次建模。其中的每个基本图段用一个显示列表定义,高层图段是将低层图段的显示列表按照一定的位置、方向和尺寸调用构成。
4. 显示列表的删除
显示列表的删除可以使用函数:
void glDeleteLists(GLuint listID, GLsizei range);
删除从标识listID开始的range个相邻的显示列表,如果指定的显示列表未被创建,将被忽略。
- 基本图形算法:
DDA 数值微分法
原理:由于直线的一阶导数是连续的,而且对于△x和△y是成正比的,故此可以通过在当前位置上分别加上二个小增量来求下一点的x,y坐标,如下图所示。
由于在光栅化的过程中不可能绘制半个像素点,因此对求出的xi+1,yi+1的值需要四舍五入。
在一个迭代算法中,如果每一步的x、y值是用前一步的值加上一个增量来获得的,那么,这种算法就称为增量算法。
DDA算法的特点:DDA算法是一个增量算法,直观、易实现,但不利于用硬件实现。
- 中点Bresenham算法
此时直线将平面分成三个区域:对于直线上的点,F(x, y)=0;对于直线上方的点,F(x, y)>0;对于直线下方的点,F(x, y)<0,如下图所示。
基本原理:根据直线的斜率确定或选择变量在x或y方向上每次 递增一个单位,而另一方向的增量为1或0,它取决于实际直线与 相邻像素点的距离,这一距离称为误差项。
如下图所示,假定0≤k≤1,x是最大位移方向。算法每次在x方向上 加1,y方向上加0或加1。设当前点是P(xi, yi),则下一个点在Pu(xi+1, yi+1) 和Pd(xi+1, yi)中选一。以M点表示Pu与Pd的中点,又设Q点是理想直线与 垂线x=xi+1的交点,根据Q点与M点的位置判断选取哪一个点。
改进的Bresenham算法
基本原理:假定直线段的0≤k≤1,如下图所示,过各行、各列像素中心构造一组虚拟网格线,按直线起点到终点的顺序 计算直线与各垂直网格线的交点,交点与网格线的误差值为d。
当d>0.5时,直线更接近于像素点(x+1, y+1),当d<0.5时,更接近于 (x+1,y);当d=0.5时,约定取(x+1, y)。
图5-4 改进的Brensemham算法绘制直线的原理
误差项d的初始值为0,每走一步有d=d+k,一旦y方向上走了一步,就要把d减去1。即有:
算法步骤:
(1)输入直线的两端点P0(x0, y0)和P1(x1, y1)。
(2)计算初始值△x、△y、e=-△x、x=x0、y=y0。
(3)绘制点(x, y)。
(4)e更新为e+2△y,判断e的符号。若e>0,则(x, y)更新为(x+1, y+1),同时将e更新为e-2△x;否则(x, y)更新为(x+1, y)。
(5)当直线没有画完时,重复步骤3和4。否则结束。
- 八分画圆
由于圆心位于原点的圆的有四条对称轴x=0,y=0,x=y,x=-y,如下图所示,这样只要知道圆上的任意一点,就可以得到圆上关于对称轴其他7个点。
这样,要得到整个圆的扫描转换像素集,只要扫描转换1/8圆弧即可,这通常称为八分法画圆。
解决的问题转化为扫描如下图所示的1/8段圆弧。
同样考虑第一象限内直线x=0与x=y之间的1/8圆弧,利用圆的极坐标方程,计算公式如下:
中点Bresenham画圆
构造函数F(x,y)=x2+y2-R2。这样,对于圆上的点,有F(x, y)=0;对于圆外的点,F(x, y)>0;而对于圆内的点,F(x,y)<0。
1.算法原理
此时最大位移方向为x,每次x方向上走一步,而y方向上或减1,或减0。假定当前与圆弧最近者已确定,为P(xi, yi),下一候选点只能是(xi+1, yi),或(xi+1, yi-1),到底选哪一个依旧用中点判断。
2.构造判别式
M的坐标为:M(xi +1, yi-0.5),当F(xM, yM)<0时,取Pu(xi+1, yi);当 F(xM, yM)>0时,取Pd(xi+1, yi-1),当F(xM, yM)=0时,约定取Pu。
构造判别式:d=F(xM, yM)=F(xi+1, yi-0.5)=(xi+1)2+(yi-0.5)2-R2
当d≤0时,下一点取Pu(xi+1, yi);
当d>0时,下一点取Pd(xi+1, yi-1)。
6. 算法步骤
(1)输入圆的半径R。
(2)计算初始值d=1-R、x=0、y=R。
(3)绘制点(x,y)及其在八分圆中的另外七个对称点。
(4)判断d的符号。若d<0,则先将d更新为d+2x+3,再将(x, y)更新为(x+1, y);否则先将d更新为d+2(x-y)+5,再将(x, y)更新为(x+1, y-1)。
(5)当x<y时,重复步骤3和4。否则结束。
多边形的扫描转换
图形学中多边形有两种表示方法:多边形的顶点表示与点阵表示。 顶点表示用多边形的顶点序列来刻画多边形;点阵表示则是用位于多边形内的像素的
集合来刻画多边形。
扫描转换多边形或多边形的填充:从多边形的顶点信息出发,求出位于其内部的各个像素,并将其颜色值写入帧缓存中相应单元的过程。
边缘填充算法
1. 边缘填充算法
基本思想:按任意顺序处理多边形的每条边。处理时,先求出该边与扫描线的交点,再对扫描线上交点右方的所有像素取反。
特点:算法简单,但对于复杂图型,每一像素可能被访问多次。
2.栅栏填充算法
栅栏指的是一条过多边形顶点且与扫描线垂直的直线。它把多边形分为两半。
基本思想:按任意顺序处理多边形的每一条边,但处理每条边与扫描线的交点时,将交点与栅栏之间的像素取反。
特点:这种算法尽管减少了被重复访问像素的数目,但仍有一些像素被重复访问。
3.边标志算法
基本思想:先用特殊的颜色在帧缓存中将多边形的边界勾画出来,然后将着色的像素点依x坐标递增的顺序配对,再把每一对像素构成的区间置为填充色。
操作分为两个步骤:
(1)打标记:对多边形的每条边进行直线扫描转换。
(2)填充:对每条与多边形相交的扫描线,依从左到右的顺序,按“左闭右开”的原则对扫描线上的像素点进行填色。
特点:当用软件实现本算法时,速度与改进的有效边表算法相当,但本算法用硬件实现后速度会有很大提高。
Opengl中的变换
OpenGL中包含了两个重要的矩阵:模型视图矩阵和投影矩阵,其中模型视图矩阵用于物体的模型视图变换,投影矩阵用于投影变换。
视图变换主要用于确定观察参考坐标系,即确定视点的位置和观察方向。默认情况下,观察坐标系与用户坐标系重合,此时视点位于原点,观察方向为用户坐标系z轴的负向。
变换实例
#include <gl/glut.h> void Initial() { glEnable(GL_DEPTH_TEST); // 启用深度测试 glClearColor(1.0f, 1.0f, 1.0f, 1.0f ); //背景为白色 } void ChangeSize(int w, int h) { if(h == 0) h = 1; glViewport(0, 0, w, h); // 设置视区尺寸 glMatrixMode(GL_PROJECTION); // 指定当前操作投影矩阵堆栈 glLoadIdentity(); // 重置投影矩阵 GLfloat fAspect; fAspect = (float)w/(float)h; // 计算视区的宽高比 gluPerspective(45.0, fAspect, 1.0, 500.0); // 指定透视投影的观察空间 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void Display(void) { static float fElect1 = 0.0f; // 绕原子旋转的角度 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色和深度缓冲区 glMatrixMode(GL_MODELVIEW); // 指定当前操作模型视图矩阵堆栈 glLoadIdentity(); // 重置模型视图矩阵 glTranslatef(0.0f, 0.0f, -250.0f); //将图形沿z轴负向移动 glColor3f(1.0f, 0.0f, 0.0f); glutSolidSphere(12.0f, 15, 15); // 绘制红色的原子 glColor3f(0.0f, 0.0f, 0.0f); glPushMatrix(); // 保存当前的模型视图矩阵 glRotatef(fElect1, 0.0f, 1.0f, 0.0f); // 绕y轴旋转一定的角度 glTranslatef(90.0f, 0.0f, 0.0f); // 平移一段距离 glutSolidSphere(6.0f, 15, 15); // 画出第一个电子 glPopMatrix(); // 恢复模型视图矩阵 glPushMatrix(); // 保存当前的模型视图矩阵 glRotatef(45.0f, 0.0f, 0.0f, 1.0f); //绕z轴旋转45° glRotatef(fElect1, 0.0f, 1.0f, 0.0f); glTranslatef(-70.0f, 0.0f, 0.0f); glutSolidSphere(6.0f, 15, 15); // 画出第二个电子 glPopMatrix(); // 恢复模型视图矩阵 glPushMatrix(); // 保存当前的模型视图矩阵 glRotatef(-45.0f,0.0f, 0.0f, 1.0f); //绕z轴旋转-45° glRotatef(fElect1, 0.0f, 1.0f, 0.0f); glTranslatef(0.0f, 0.0f, 60.0f); glutSolidSphere(6.0f, 15, 15); // 画出第三个电子 glPopMatrix(); fElect1 += 10.0f; // 增加旋转步长,产生动画效果 if(fElect1 > 360.0f) fElect1 = 10.0f; glutSwapBuffers(); } void TimerFunc(int value) { glutPostRedisplay(); glutTimerFunc(100, TimerFunc, 1); } int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutCreateWindow("分子动画示例"); glutReshapeFunc(ChangeSize); glutDisplayFunc(Display); glutTimerFunc(500, TimerFunc, 1); //指定定时器回调函数 Initial(); glutMainLoop(); return 0; }
曲线曲面
曲线被表示为参数t的矢量函数。
曲面表示成为双参数u和v的矢量函数。
相对非参数表示方法,参数表示方法更能满足形状数学描述的要求,因而具有更好的性能
图8-1 曲线的拟合 图8-2 曲线的逼近
如图8-1所示,当用一组型值点来指定曲线曲面的形状时,形状完全通过给定的型值点列。用该方法来得到曲线曲面称为曲线曲面的拟合。而当用一组控制点来指定曲线曲面的形状时,求出的形状不必通过控制点列,该方法称为曲线曲面的逼近,如图8-2所示。另外,求给定型值点之间曲线上的点称为曲线的插值。一般,将连接有一定次序控制点的直线序列称为控制多边形或特征多边形,如图8-2中的虚线所示。
样条曲线是指由多项式曲线段连接而成的曲线,在每段的边界处满足特定的连续条件。样条曲面则可以用两组正交样条曲线来描述。
beier曲线:
Bezier曲线由一组控制多边形折线(控制多边形)顶点惟一地定义。如图8-4所示,在控制多边形的各顶点中,只有第一个和最后一个顶点在曲线上,其他的顶点则用以定义曲线的导数、阶次和形状。由于曲线的形状趋向于控制多边形的形状,所以改变多边形的顶点就会改变曲线的形状,这就使观察者对输入、输出关系有直观的感觉。
Bezier曲线是一个阶数比控制点少1的多项式,下面给出一阶、二阶和三阶Bezier曲线的表达式。
1.一次Bezier曲线
当n=1时,有两个控制点P0和P1,Bezier多项式是一个一次多项式:
于是,一次Bezier曲线是连接起点P0和终点P1的直线段。
2.二次Bezier曲线
当n=2时,有三个控制点P0、P1和P2,Bezier多项式是一个二次多项式:
于是,二次Bezier曲线是一条抛物线。
3.三次Bezier曲线
当n=3时,有四个控制点P0、P1、P2和P3,Bezier多项式是一个三次多项式:
LOD
详细分层
利用减少场景的复杂度来挺高图像的绘制速度,而细节层次这一门用来显示和简化场景的技术就是在这产生的。
细节层次是在不改变场景视觉效果的条件下,逐步简化场景的表面细节来减少场景的几何图形锁产生之复杂性,并且它又可以提高绘制算法的效率。
从网格的几何特性着手,这种技术可以归纳为3种不同的基本推演,分别是
顶点删除,边界压缩及面的收缩。
网格简化 real time rendering