计算机图形:图元的属性

发布时间 2023-10-23 16:37:35作者: 明明1109

基本概念

任何影响图元显示方法的参数,称为属性参数(attribute parameter),如颜色、大小等图元的基本特性。特定条件属性:在特定条件下怎样显示图元。

维护属性和其他参数当前值表的图形系统,称为状态系统(state sytem)或状态机(state machine)。输出图元的属性和当前帧缓存位置等参数,称为状态变量(state variable)或状态参数(state parameter)。在给状态参数赋值时,系统进入一个特定状态,一直保留到状态参数的值改变。

状态变量

OpenGL中,状态变量有:颜色、其他图元属性、当前矩阵模式、模型观察矩阵的元素、缓存当前位置和场景光照效果参数等。这些参数都有默认值,可由独立函数修改值,在指定新值前不变。

tips:OpenGL所有图元使用当前状态表中的属性显示。改变属性只能影响这之后的图元,而之前的不受影响。

颜色和灰度

颜色是所有图元的一个基本属性。

RGB颜色分量

彩色光栅系统中,可选用的颜色数量依赖于帧缓存中提供的存储容量。
颜色有两种方式存储在帧缓存:
1)RGB编码:直接在帧缓存中存储RGB编码(常用);
2)颜色表:将颜色码存入一个独立的表中,并在像素位置存储指向颜色表表项的索引(少用)。

tips:颜色表曾经很流行,但现在硬件成本很低,较少使用。

灰度

可在APP中用RGB颜色函数设定灰度(gray scale)。当RGB指定相同量红色、绿色、蓝色时,结果是某种程度的灰色。靠近0的值,生成暗灰色;靠近1的值,生成亮灰色。


OpenGL颜色函数

OpenGL RGB和RGBA颜色模型

OpenGL颜色设定使用RGBA模式,包含红色、绿色、蓝色、α系数(alpha coefficient)4个分量,用于控制颜色调和。

可调用glutInitDisplayMode(),设置颜色显示模型(color display mode)为RGB(或RGBA)模式:

glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);

GLUT_SINGLE: 指示正在使用单个缓存;
GLUT_RGB: 设定RGB模式(默认模型)。

RGBA模式下,用glColor*来选择当前颜色分量:

glColor*(colorComponent);

*是后缀码。颜色分量的整数范围0255,浮点数范围0.01.0。

如果使用glColor3,α分量自动设为1.0,表示不需要颜色调和。

OpenGL颜色索引模式

也可用颜色索引模式(color-index mode)指定颜色。该模式下,通过指定一个指向颜色表的索引来设定当前颜色:

glIndex*(colorIndex);

颜色表索引位置数是2的指数,colorIndex表示颜色表索引位置,指定为范围内的整数即可。

OpenGL基本库没有装载颜色查找表的函数,因为表处理是窗口系统的一部分。可调用glutSetColor来设置给定索引位置指定颜色:

glutSetColor(index, red, green, blue);
// index 指定表项的索引值
// red, green, blue为0.0~1.0的浮点数

OpenGL核心库扩充了3个颜色表的子程序,是OpenGL成像子集(Imagin Subset)的一部分,表中颜色值可通过各个缓存处理来修改像素值。

OpenGL成像子集使用GL_COLOR_TABLE, GL_POST_CONVOLUTION_COLOR_TABLE, GL_POST_COLOR_MATRIX_COLOR_TABLE的特定颜色表,由glEnable激活。可通过子程序选择特定颜色表、设定颜色表的值、复制表的值或指定需要改变像素颜色的哪个分量及如何改变它。

OpenGL颜色调和

RGB/RGBA模式下,先将第一个对象装载进帧缓存,再将第二个对象的颜色与帧缓存颜色相混合来实现2个对象颜色的调和。当前帧缓存颜色,称为OpenGL目标颜色(destination color),第二个对象的颜色,称为OpenGL源颜色(source color)。

  • 激活调和特性

APP中颜色调和,先激活/禁用该OpenGL特性:

glEnable(GL_BLEND);
glDisable(GL_BLEND);
  • 调和因子
    可通过2组调和因子生成不同的颜色效果:一组针对目标对象,另一组针对源对象。
    新调和颜色 = sFactor * source(R, G, B) + dFactor * destination(R, G, B)
    结果分量会被归一到0.0~1.0之间:大于1.0的和会设为1.0,小于0.0的和设为0.0。

设置调和因子:
glBlendFunc(sFactor, dFactor);
sFactor和dFactor 源和目标因子。sFactor默认值GL_ONE, dFactor默认值GL_ZERO。

  • 常量调和因子

L_ZERO=(0.0, 0.0, 0.0, 0.0), GL_ONE=(1.0, 1.0, 1.0, 1.0)。
GL_DST_ALPHA或GL_SRC_ALPHA 将4个调和因子设为目标α或源α值;
GL_ONE_MINUS_SRC_ALPHA或GL_ONE_MINUS_DST_ALPHA 用1.0减去源或目标颜色α。

OpenGL颜色数组

可以在顶点数组和坐标值混合来指定场景的颜色值。

  • 激活OpenGL颜色数组
glEnableClientState(GL_COLOR_ARRAY);
  • 指定颜色分量的位置和格式
glColorPointer(nColorComponents, dataType, offset, colorArra);

nColorComponents:赋值3或4,取决于是否在数组colorArray中列出RGB或RGBA颜色分量。
dataType:指定颜色值的数据类型,如GL_INT或GL_FLOAT。
offset:对于单独的颜色数组,赋值0;对于颜色数据和顶点数据混合在同一数组,这offset的值是数组中每组颜色分量的字节数。

1)使用2个单独数组:顶点数组、颜色数组,绘制立方体

typedef GLint vertex3[3], color3[3];

vertex3 pt[8] = { {0,0,0}, {0,0,0}, {1,0,0},
    {1,1,0}, {0,0,1}, {0,1,1}, {1,0,1}, {1,1,1}};
color3 hue[8] = { {1,0,0}, {1,0,0}, {0,0,1},
    {0,0,1}, {1,0,0}, {1,0,0}, {0,0,1}, {0,0,1}};

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(3, GL_INT, 0 ,pt);
glColorPoint(3, GL_INT, 0, hue);

2)使用一个交错数组存放顶点、颜色数据

static GLint hueAndPt[] = { // color-vertex
    1,0,0, 0,0,0, 1,0,0, 0,0,0, 
    0,0,1, 1,0,0, 0,0,1, 1,1,0, 
    1,0,0, 0,1,1, 1,0,0, 0,1,1,
    0,0,1, 1,0,1, 0,0,1, 1,1,1,
};

glVertexPointer(3, GL_INT, 6*sizeof(GLint), hueAndpt[3]);
glColorPointer(3, GL_INT, 6*sizeof(GLint), hueAndpt[0]);

颜色值从交错数组第一个元素(hueAndpt[0])开始,顶点值从第4个元素(hueAndpt[3])开始;第一个顶点和第二个顶点在交错数组中相隔6*sizeof(GLint)byte。

  • 一次性指定顶点、颜色数组、其他类型信息

如果不想单独激活、设置顶点、颜色数组,可以调用下面函数一次性激活:

glInterleavedArrays(GL_C3F_V3F, 0, hueAndpt);

GL_C3F_V3F:指定颜色(C)、顶点(V)的三元素浮点数描述。hueAndpt数组按颜色在坐标前的方式交叉存放。

  • 颜色索引模式下,定义一个颜色索引数组

glIndexPointer(type, stride, colorIndex);
colorIndex:颜色索引;
stride, colorIndex含义与glColorPointer相同。

其他OpenGL颜色函数

  • 为窗口选择RGB颜色分量

指定所有颜色缓存的颜色:

glClearColor(red, green, blue, alpha);

每个分量都是0.0~1.0的浮点数,4个参数默认值0(黑色)。第4个参数仅在激活了OpenGL调和特性后,才能对前面颜色和当前颜色调和。颜色调和不可能作用于颜色表指定的值。

  • 清除颜色缓冲区

glClearColor指定清除色,glClear(GL_COLOR_BUFFER_BIT)执行颜色清除动作

glClear(GL_COLOR_BUFFER_BIT);

glClear也可以用来清除其他缓存,重设初值:
1)GL_DEPTH_BUFFER_BIT 深度缓存;
2)GL_ACCUM_BUFFER_BIT 累计缓存;
3)GL_STENCIL_BUFFER_BIT 模板缓存。

  • 颜色索引模式下,用设置窗口颜色
glClearIndex(index); // 设置清除颜色索引
glClear(GL_COLOR_BUFFER_BIT); // 清除窗口颜色缓存

点的属性

可设置点属性:颜色、大小。

对于一个状态系统,点的显示颜色和大小由存放在属性表中的当前值确定,颜色分量用RB值或颜色表的索引设定。
对于光栅系统,点的大小是像素大小的整数倍,因此一个大的点显示成一个像素方块。

OpenGL点属性函数

  • 指定点位置的显示颜色

颜色由glColor/glIndex设定。

  • 指定点大小

点以像素方块的形式显示。点大小默认1.0,显示一个像素;为点大小2.0时,显示2x2的像素阵列。

glPointSize(size);

size:正浮点数。


线的属性

直线段3个基本属性:颜色、线宽、线型。
线的颜色用对所有图元相同的glColor*函数,线宽、线型用单独的函数。此外,线还能生成如画笔、画刷等效果。

  • 线宽
    光栅实现中,可用类似于Bresenham算法,通过在每个取样位置处使用一个像素来生成标准线宽。其他线宽是标准线宽的整数倍,通过沿相邻平行线路径绘制额外的像素而显示。

  • 线型
    实线、虚线、点线等。可通过设置沿线路径显示的实现线段的长度和间距,来修改画线算法,生成各种类型的线。如,可通过在实线段之间插入等长空白段显示虚线。

OpenGL线属性函数

  • 线宽函数

设置线宽度。

glWidth(width);

width:线宽度,浮点数类型。输入值0.0,则线段用默认标准宽度1.0显示。

  • 线型函数

直线段默认显示实线,通过glLineStipple可显示划线、点线、短划和点混合的线段等。

glLineStipple(repeatFactor, pattern);

pattern:描述如何显示线段,16bit整数。bit值1对应1个“开”像素,值0对应1个“关”像素。模式从低位开始用于线路径,默认模式0xFFFF(每位均为1),生成实线。
repeatFactor:说明模式中每bit重复应用多少次,才轮到下一位。默认值1。

  • 激活线型特性

使用线型显示段前,必须先激活其特性:

glEnable(GL_LINE_STIPPLE);

如果忘记激活,这显示实线(pattern = 0xFFFFF)。
关闭线型特性:

glDisable(GL_LINE_STIPPLE);

其他线效果

颜色渐变显示线段。定义线段时,为2个端点赋予不同颜色,绘制线段时,将按两端点颜色线性插值进行显示:

glShadeModel(GL_SMOOTH);

glBegin(GL_LINES);
glColor3f(0.0, 0.0, 1.0);
glVertex2i(50, 50);
glColor3f(1.0, 0.0, 0.0);
glVertex2i(250, 250);
glEnd();

glShadeModel参数:

  • GL_SMOOTH:选择平滑着色
  • GL_FLAT:选择平底着色

填充区属性

多数图形软件包限制填充区为多边形或凸多边形,因为能用线性方程描述。

填充模式

填充模式:将一区域显示为单一颜色、填充图案,或只给出边界的“空心”模式。也能用笔刷模式、颜色调和、纹理,对指定区域进行填充。

填充区域需要指出边界。对多边形来说,可使用不同的颜色、线宽、线型给出边界。还能为区域前向面、后向面选择不同的显示属性。

填充图案可以用一个矩形颜色阵列(即点阵)表示,每个位置指定一个颜色。

从初始位置开始,填充图案在水平、垂直方向反复填充,直到显示区域都填满了无重叠的填充图案。这种填充处理,称为平铺(tiling),矩形填充图也叫平铺图案(tiling pattern)。

颜色调和填充区域

软填充(soft-fill)或色彩填充(tint-fill)算法:将填充图案和背景颜色混合,使用透明因子确定背景中有多少应该混合到颜色中。

调和颜色的填充方法,作用:
1)减弱在已经模糊的对象边界上的填充颜色,实现对边的反走样;
2)允许对原来用半透明笔刷填充的颜色区域进行重新涂色。

填充区属性函数

OpenGL仅提供对凸多边形填充区子程序。显示一个填充凸多边形步骤:
1)定义一个填充图案;
2)引用多边形填充子程序;
3)激活OpenGL多边形填充特性;
4)描述要填充的多边形。

多边形填充图案一直显示到包括多边形的边。因此,填充区没有边界线,除非指定显示。

多边形内部有很多选项可显示:
1)填充图案;
2)空闲多边形;
3)只显示顶点,而且没有内部填充、没有边;
4)为填充区的前向面和后向面指定不同的属性。

  • OpenGL填充图案函数

默认时,凸多边形用当前颜色设定显示成一个实心颜色区域。可以用32x32的位掩模,用图案填充多边形:掩模中bit值为1,表示对应像素设为当前颜色;值为0表示对应的帧缓存位置的值不变。掩模数据类型GLubyte。

有了掩模后,调用glPolygonStipple将其用做当前填充图案:

glPolygonStipple(fillPattern);
  • 激活填充子程序

使用当前图案填充多边形顶点前,必须激活:

glEnable(GL_POLYGON_STIPPLE);
  • OpenGL纹理和插值图案

填充多边形另一个方法:纹理。纹理可看做材料外貌的图案,可以对多边形的顶点赋予不同颜色,而插值填充用来为在各种光照条件下的着色表面生成真实感。

例,将蓝、红、绿分别赋给三角形的三个顶点,多边形填充就是在三个顶点间颜色插值:

glShadeModel(GL_SMOOTH); // 默认, 顶点插值

glBegin(GL_TRIANGLES);
glColor3f(0.0, 0.0, 1.0,);
glVertex2i(50, 50);
glColor3f(1.0, 0.0, 0.0);
glVertex2i(150, 50);
glColor3f(0.0, 1.0, 0.0);
glVertex2i(75, 150);
glEnd();
  • OpenGL线框图方法

可以通过显示一组顶点来显示多边形,而无填充:

glPolygonMode(face, displayMode);

face:指定在多边形哪个面仅显示边或顶点,值可为GL_FRONT, GL_BACK或GL_FRONT_AND_BACK。
displayMode:指定显示边或顶点,值可为GL_POINT, GL_LINE,GL_FILL(默认)。

可以为多边形填充内部时,同时用颜色或图案显示边或顶点。

// 绘制六边形, 填充和边用不同颜色

glColor3f(0.0, 1.0, 0.0);
/* Invoke polygon-generating routine */
glBegin(GL_POLYGON);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glVertex2iv(p6);
glEnd();

glColor3f(1.0, 0.0, 0.0);
glPolygonMode(GL_FRONT, GL_LINE);
/* Invoke polygon-generating routine again */
glBegin(GL_POLYGON);
glVertex2iv(p1);
glVertex2iv(p2);
glVertex2iv(p3);
glVertex2iv(p4);
glVertex2iv(p5);
glVertex2iv(p6);
glEnd();
  • 边缝线

对于一个3D多边形,这种显示填充多边形边的方法可能在边之间生成缝隙,称为缝线(stitching)的效果,由扫描填充算法和边的画线算法计算差别造成。
3D多边形填充时,深度值(离xy平面的距离)按每一(x,y)位置计算。但该深度值通常与同一(x,y)位置用画线算法计算所得的深度值不完全相同。因此,进行可见性测试时,内部填充色可用来代替边的颜色,以显示沿多边形边界的点。

消除边缝隙方法:
1)移动由填充子程序计算的深度值,使它们与多边形的边深度值不重叠。

glEnable(GL_POLYGON_OFFSET_FILL);  // 激活扫描线填充的位移子程序
glPolygonOffset(factor1, factor2); // 设定一对计算深度位移总量的浮点值factor1, factor2

根据factor1, factor2计算深度位移:

depthOffset = factor1 * maxSlope + factor2 * const

其中,maxSlope是多边形的最大斜率,const是实现常数。xy平面上多边形,斜率为0;否则,maxSlope= △多边形深度 / △x或△y。factor1, factor2典型值是0.75, 1.0。

2)用OpenGL的模板缓存来限制多边形内部填充,使它和边不重叠。复杂且慢,多数使用深度位移法。

  • 消除选定边

OpenGL提供从线框图中消除选定边的机制。用glEdgeFlag设置该顶点是否通过边界上的边与下一顶点连接的标志位,来决定是否显示边的开关。

glEdgeFlag(flag);

flag: GL_FALSE 其后所有顶点的边不再显示;GL_TRUE 其后所有顶点的边显示。

例:下面程序将△v1v2v3线框图显示为2条边

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

glBegin(GL_POLYGON);
glVertex3fv(v1);
glEdgeFlag(GL_FALSE); // 这之后的顶点对应边不显示
glVertex3fv(v2);
glEdgeFlag(GL_TRUE);  // 这之后的顶点对应边显示
glVertex3fv(v3);
glEnd();

多边形标志也可以通过数组指定,可以与顶点数组混合或作为附加数组。创建一个边标志数组:

glEnableClientState(GL_EDGE_FLAG_ARRAY);
glEdgeFlagPointer(offset, edgeFlagArray);

offset:指出数组edgeFlagArray中边标志值之间的字节数。默认值0。

  • 前向面函数

默认由多边形顶点次序控制前向面、后向面,但还是可以单独指定:

glFrontFace(vertexOrder);

vertexOrder:GL_CW 则随后定义的顺时针多边形可看做前向面;GL_CCW 顶点的逆时针次序为前向面。


字符属性

显示的字符由字体、大小、颜色、方向属性控制。可对单个字符,或整个字符串(文本)设置属性。

可供程序员用的文本选项:
1)选择字体
NewYork、Courier、Helvetica、London、Times Roman等特定风格的一组字符和其他一些特殊的符号组。字体的字符可以用下划线风格(实线、点线、双线)、黑体、斜体、轮廓或影线风格。

  • 颜色
    文本颜色设置存储在系统属性表中,由将字符定义装入帧缓存的程序使用。显示字符串时,用当前颜色来设置与字符形状和位置相对应的帧缓存中的像素值。

  • 大小
    可通过缩放字符的整体尺寸(高度、宽度)或者仅缩放字符高度或宽度,来调整文本大小。字符大小由打印机以磅(point)为单位制定,1磅是0.035146cm。

磅值指定字符体的大小,但不同字体(即使磅值相同)有不同的大小。同一种字体中,所有字符的底线(bottomline)和顶线(topline)间的距离相同,但字符体宽度可能不同。这成比例间距字体(proportionally spaced font)中,窄字符i、j、l、f的字符体宽度比宽字符W或M要小。

字符高度(character height)定义为字符基线(base-line)和帽线(capline)之间的距离。如下图,像f、j这样的有核字体,通常超出字符体的限制,下行字符(g、j、p、q、y)要扩展到基线以下。

字符间隔是字符串经常要设定的一个属性。

  • 方向
    字符串的方向按字符向上向量(character up vector)设定。字符串会显示成字符底线到帽线的方向与向上向量一致。

(a)是字符向上向量的方向 (b)是字符串的显示方向。两者垂直。

字符串方向(文本方向)经常设置成与向上向量一致,水平或者垂直。用向上向量与文本路径结合的方法,可以生成倾斜的文本。

  • 对齐
    指定如何依赖参考坐标定位文本。如,单字符按基线或字符中心对齐。

水平和垂直字符对齐:

字符串对齐:

OpenGL字符属性函数

2种方法实现字符:
1)核心库的位图函数设计字体集;
2)引用OpenGL字符生成函数。

点阵或轮廓字体,显示颜色由当前颜色状态确定。字符间隔、大小由字符描述确定,如GLUT_BITMAP_9_BY_15和GLUT_STROKE_MONO_ROMAN;glLineWidth可为轮廓字体选择线宽,glLineStipple选择线型.

OpenGL反走样函数(抗锯齿)

取样过程,将物体上的坐标点位置离散化时,光栅算法生产的图元有锯齿或阶梯状外观。这种由于低频采样造成的信息失真,称为走样(aliasing),可用反走样(antiasing)方法改善。

  • 激活反走样
glEnable(primitiveType);

primitiveType:GL_POINT_SMOOTH(点), GL_LINE_SMOOTH(线), GL_POLYGON_SMOOTH(多边形),根据要抗锯齿的图元类型选择。

OpenGL查询函数

查询函数(query function)能获得包括属性设定在内的任意状态参数的当前值,函数将状态值复制到数组中,便于以后使用或者检查当前的系统状态是否有错误。

查询当前属性值,调用合适的glGet函数(取决于要查询的属性值类型),如:

glGetBooleanv();
glGetFloatv();
glGetIntegerv();
glGetDoublev();

这类函数有2个变量:
1)标识一个属性或状态参数的OpenGL符号常量;
2)指向函数名指出的数据类型的一个数组。

glGetFloatv(GL_CURRENT_COLOR, colorValues); // 获取当前RGBA浮点数设定

还能获取其他当前状态值,如GL_POINT_SIZE,GL_LINE_WIDTH,GL_CURRENT_RASTER_POSITION等。GL_POINT_SIZE_RANGE,GL_LINE_WIDTH_RANGE:支持对点大小和线的宽度检查。


OpenGL属性组

属性和其他状态参数按属性组(attribute group)进行组织,每组包括相关的状态参数集合,如,

  • 点属性组(point-attribute group)包括大小和点的平滑(反走样)参数
  • 线属性组(line-attribute group)包括宽度、模板状态、模板图案、模板重复计数、线段光滑状态等
  • 多边形属性组(polygon-attribute group)包括11种多边形参数,如填充模式,前向面标志,多边形平滑状态

OpenGL有20多个不同的属性组,可用一个函数保存或重新设定一个或多个组内所有参数。

  • (服务器)属性栈

保存一个属性组内所有参数,放进属性栈(attriute stack):

glPushAttrib(attrGroup);

attrGroup:标识一个属性组的符号常量,如GL_POINT_BIT, GL_LINE_BIT, GL_POLYGON_BIT。GL_CURRENT_BIT:保存颜色参数;GL_ALL_ATTRIB_BITS:存储所有属性组中等所有状态参数。参数支持逻辑OR操作。

状态参数存储后,可对属性栈中所有值重建:

glPopAttrib(); // 无需参数
  • 客户属性栈

保存和重建客户状态参数。存取堆栈用glPushClientAttrib和glPopClientAttrib。只有2个:1)用于像素存储器模式;2)用于顶点数组。


小结

  • 属性控制图元显示特征。属性值以状态变量形式存储,图元用当前状态值生成。状态变量改变时,仅影响后面定义的图元。
  • 颜色是所有图元的公共属性,常用RGB(RGBA)描述。红绿蓝值存储在帧缓存中,用于控制RGB监视器的3个电子枪。颜色也可用颜色表实现(较少)。帧缓存中一个颜色只是一张表的一个索引,源表项存储RGB值。颜色表可在不增加帧缓存容量时,提供大范围的可选颜色。
  • 基本点属性:颜色、大小。线属性:颜色、宽度、类型。线宽用标准单像素宽度倍数指定。线型属性:实线、划线、点线、各种画刷/画笔类型。
  • 填充区可用颜色调和填充,用于反走样、绘图软件包。
  • 字符可按不同颜色、大小、间隔、方向显示。为设置字符串的方向,我们选择字符向上向量的方向和文本路径的方向。
  • 扫描转换是光栅系统数字化过程,因此图元有阶梯效应(锯齿)。可通过调整像素强度的反走样过程来改善。
  • OpenGL图元属性值由状态变量维护。一次属性设定保证之后定义的图元都有效,直到再次改变。