gl函数流程图

1.清除

首先设置清除颜色缓冲区、深度缓冲区和模板缓冲区的值,这一步只是设置而并非真正的清除,每一个缓冲区的清除值都有默认值。之后调用glClear函数,通过设置参数,用上述设置的清除值清除指定的缓冲区。

img

2.纹理贴图

首先使用sgpugl_glGenTextures创建纹理对象,纹理对象的名称空间是无符号整数,纹理对象的名称仅限于在sgpugl_glGenTextures使用。sgpugl_glActiveTexture指定需要激活的纹理单元,之后的操作都在该纹理单元下进行(如果仅一个纹理单元默认为GL_TEXTURE0,可以指定多个纹理单元)。sgpugl_glBindTexture将纹理对象绑定在纹理目标上。如纹理对象texture1绑定在纹理单元GL_TEXTURE0下的纹理目标GL_TEXTURE_2D上。接下来的操作都是针对该纹理目标进行(一个纹理单元下可以有多个纹理目标)。sgpugl_glTexImage(12)D为该纹理目标指定纹理图片,生成纹理。sgpugl_glTexParameteri设置纹理参数,如环绕方式、过滤方式等。

在绘制前需要指定激活的纹理单元。采样器的类型识别纹理单元上的纹理目标。例如,采样器类型为的sampler2D,则采样器会在先前指定的纹理单元上选择纹理目标TEXTURE_2D,进而对纹理进行采样。

如下图是将三个纹理对象绑定在两个纹理单元下的三个纹理目标上,用其中一个纹理进行绘制的流程图。假如Draw前指定激活的纹理单元为GL_TEXTURE0,定位到红色区。假如着色器中采样器类型为的sampler2D,则定位到蓝色区域,找到对应纹理进行采样。

其中绿色区域的函数可以用指定的数据修改纹理目标指定区域的内容。

img

3.深度测试,多边形深度偏移

用1中的方法清除深度缓冲区,此处以默认值为例。在进行深度测试前必须先用sgpugl_glEnable启用深度测试功能。顶点X,Y坐标会从标准化设备坐标经过视口变换变成窗口坐标。z值根据sgpugl_glDepthRange(f)设置的范围也会从标准化设备坐标映射到窗口坐标,两个范围参数都限制在[0,1]。光栅化之后深度测试之前,如果较多片元的深度相差非常小,例如在相同的深度上绘制两个重叠的三角形,会发生z-Fighting。因此需要使用sgpugl_glEnable启用偏移,再通过sgpugl_glPolygonOffset对绘制的片元的深度值(由顶点插值得到)进行整体偏移。深度测试时通过sgpugl_glDepthFunc设置深度缓冲比较函数,默认为GL_LESS,即当前绘制的片元深度如果小于深度缓冲对应位置的深度值,则将颜色值写入颜色缓冲(之后可能被覆盖或者混合),否则丢弃。保留片元的深度值是否更到深度缓冲,通过glDepthMask设置。最后根据情况关闭深度测试和偏移。

img

4.颜色混合,Alpha测试

如果要绘制完全透明的物体,可以将全透明物体的Alpha值设置为0,将不透明物体的Alpha值设置为1。利用sgpugl_glEnable启用透明测试,透明测试在模板测试之前。sgpugl_glAlphaFunc可以指定透明度测试函数,此处以CL_GREATER,预设值为0.1为例。如果当前绘制的片元的Alpha为0,小于预设值,片元被丢弃。如果当前绘制的片元的Alpha为1,大于预设值,则保留片元。通过保留和丢弃片元来实现完全透明的效果。最后根据情况关闭透明测试。透明测试还常用于去除纹理图白色背景。

如果要绘制半透明物体,需要使用颜色混合(不透明物体直接颜色覆盖),该步骤在所有测试之后。绘制时要先绘制不透明物体(开启深度测试并允许深度写入)之后关闭深度写入,最后按照从远到近的顺序绘制半透明物体,不然可能会出现绘制错误。

情况1:半透明物体A在不透明物体B后

情况2:半透明物体A在不透明物体B前

情况3:半透明物体A在半透明物体B前

在情况1下,先绘制B再绘制A,且A不开启深度测试,不管A是否允许深度写入,A被遮挡的部分都会绘制出来。因此需要开启深度测试。在情况3下,先绘制A再绘制B,且A不禁止深度写入,B被遮挡的部分会被直接丢弃。因此需要禁止深度写入。在情况2下,假设先绘制A再绘制B,由于A禁止深度写入,B被遮挡的部分会绘制并覆盖A。因此需要先绘制不透明物体。在情况3下,先绘制A再绘制B,绘制B时,A为目标色B为源色;先绘制B再绘制A,绘制A时,B为目标色A为源色;根据混合算法的设置,这两次的结算结果可能不同。因此需要按照顺序绘制。

利用sgpugl_glEnable启用颜色混合,sgpugl_glBlendFunc可以指定颜色混合方式,即如何将深度测试保留的片元颜色值与颜色缓冲区对应的已存在的颜色值进行计算得到新的颜色值。最后根据情况关闭颜色混合或再次开启深度写入。

img

5.颜色填充

可以使用sgpugl_glColorPointer或者sgpugl_glColor设置顶点颜色数据,在使用sgpugl_glColorPointer前需先开启顶点颜色属性。但是sgpugl_glColorPointer优先级高于sgpugl_glColor,当使用sgpugl_glColorPointer时,不管sgpugl_glColor使用在sgpugl_glolorPointer前还是后,最后绘制出的颜色为顶点颜色数组定义的颜色。sgpugl_glColor可以对sgpugl_glVertexPointer定义的一组顶点数据设置统一的颜色,也可以在sgpugl_glBegin和sgpugl_glEnd中对每个顶点设置单独的颜色。sgpugl_glColorMask允许或禁⽌帧缓冲区某种通道的写⼊颜色缓冲。如果不允许R通道写入颜色缓冲,如果设置颜色为(1,0,1)紫色,最后绘制出来的会是(0,0,1)蓝色。注意如果着色模式不是平滑模式,则片元的颜色由图元的单个顶点得到。

img

6.模板测试

模板测试在深度测试之前。需要先清空模板缓冲区(清除值默认为0,每个值都为8位二进制,即有256种取值方式),并用sgpugl_glEnable启用模板测试。sgpugl_glStencilFunc设置模板测试的规则,如默认的“GL_ALWAYS,0, 0xFF”表示总是通过模板测试;下图中的“GL_NOTEQUAL,1,0xFF”表示如果片元对应模板缓冲的值不等于1,则通过模板测试。但是在比较之前,需要将模板缓冲的值和1分别与第三个参数0xFF进行与运算。sgpugl_glStencilOp设置如何更新模板缓冲,三个参数分别表示:模板测试失败时、模板测试通过但深度测试失败时及模板测试和深度测试都通过时采取的行为。如默认“GL_KEEP,GL_KEEP,GL_KEEP”表示无论测试结果如何,模板缓冲内的值都保持不变;下图中的“GL_KEEP,GL_KEEP,GL_REPLACE”表示当深度测试通过后,片元对应模板缓冲的值用sgpugl_glStencilFunc的第二个参数值代替。sgpugl_glStencilMask设置是否允许写入模板缓冲。如默认“0xFF”表示在写入模板缓冲之前,将写入的数和0xFF进行与运算(最后数保持原状,即允许写入)。如过设置“0x00”,写入的数会变成全0,即禁止写入,相当于sgpugl_glDepthMask(GL_FALSE)。也可以根据情况设置其他8位掩码。最后根据情况关闭模板测试。模板测试可用于绘制边框。

img

7.多边形正面判断,正背面剔除

在裁剪阶段,背面剔除用于绘制观察者所看到的面,不绘制看不到的面。这使得渲染的性能上可提高超过50%。gl按照如下方式区分图元的正面和背面:

正面:顶点连接顺序按照逆时针

背面:顶点连接顺序按照顺时针

Sgpugl_glFrontFace可以修改上述规则。注意顶点连接的顺序是相对于观察者而言的。因此图元是否需要剔除,是顶点连接顺序和观察者位置共同决定的。

img

8.渲染上下文的创建和管理

img

9.裁剪测试

裁剪测试在透明测试之前。先用sgpugl_glEnable启用裁剪测试,再用sgpugl_glScissor设置裁剪框,类似sgpugl_glViewport在窗口框出一块区域。但是sgpugl_glScissor是在裁剪框内的才进行绘制。sgpugl_glViewport相当于变相重置本次绘制的窗口,将所有需要绘制的物体映射在视口区域内。

img

10.顶点数据、矩阵变换、裁剪和视口

可以使用sgpugl_glVertexPointer或者在sgpugl_glBegin和sgpugl_glEnd中用sgpugl_glVertex设置顶点坐标数据,在使用sgpugl_glColorPointer前需先开启顶点坐标属性。

顶点首先经过世界变换(M)从模型空间变到世界空间,再经过观察变换(V)从世界空间变到观察空间,之后经过投影变换(P)从观察空间变到齐次裁剪空间。其中投影变换有正交投影和透视投影(近大远小)。上述变换需要对顶点坐标进行矩阵变换,在此之前需要先设置矩阵MV矩阵和P矩阵。可以用sgpugl_glLoadMatrixf自定义当前矩阵,也可以先通过sgpugl_glLoadIdentity将当前矩阵变成单位矩阵,再通过平移、旋转、缩放或者乘以其他矩阵得到最终需要的矩阵。但是调用draw前需要切换到MV空间下。

图元组装后进行裁剪,除了视锥体裁剪和背面剔除外,还可以通过sgpugl_glClipPlane设置裁剪平面进行图元裁剪,但是要先用sgpugl_glEnable启用裁剪平面。裁剪后对顶点坐标进行齐次除法即除以w使其变到NDC空间。最后通过视口变换将顶点坐标从NDC空间映射到视口区域的屏幕空间。

img

11.光照模型

使用光照模型需要提供顶点法线数据。物体的法线向量决定了它相对于光源的方向,从而计算顶点从光源接收的光线能量。可以使用sgpugl_glNormalPointer或者sgpugl_glNormal设置顶点法线数据,在使用sgpugl_glNormalPointer前需先开启顶点法线属性。sgpugl_glNormal可以对sgpugl_glVertexPointer定义的一组顶点数据设置统一的法线,也可以在sgpugl_glBegin和sgpugl_glEnd中对每个顶点设置单独的法线。

下一步开始使用光照模型。先用sgpugl_glEnable(GL_LIGHTING)开启整体光照效果。使用sgpugl_glLightfv设置光源,即设置入射光。光源最多设置8个,从GL_LIGHT0到GL_LIGHT7,每个光源可以设置的参数如下图。其中光源位置分为方向性光源(平行光)和位置性光源(点光源),聚光灯光源方向到光源的常数衰减因子这6个属性都是针对位置性光源。用sgpugl_glEnable开启光源,如sgpugl_glEnable(GL_LIGHT0)。接下来使用sgpugl_glMaterialfv设置光照模型的材质参数,即设置材质颜色(材质颜色和光照颜色作用形成出射光)。由于物体有两面,材质可能不同,因此要先确定为哪一面设置材质,可以设置的材质参数如下图。最后用sgpugl_glLightModelfv设置光照模型,便于进行光照计算。光照模型的概念由下图所示4个部分组成。

img

12.像素操作和帧缓冲区(更高版本的opengl基本不用这些)

(1)sgpugl_glCopyPixels进行像素的复制操作,从帧缓冲区复制到帧缓冲区,不涉及内存,因此避免了显存与内存间格式转换的问题,并且加快了运算速度。该函数前四个参数表示复制像素来源的矩形的左下角坐标、宽度和高度,可以当作是在窗口确认了一块矩形区域,第五个参数用于确认复制的内容,通常使用GL_COLOR,表示复制像素的颜色,但也可以是GL_DEPTH(深度缓冲数据)或GL_STENCIL(模板缓冲数据)。通过sgpugl_glRasterPos2f设置像素绘制的起始位置,系统会将该起始位置转换成窗口坐标,之后将会从起始位置绘制出刚刚窗口上指定的矩形中的内容。

(2)首先开辟一块内存空间。sgpugl_glReadPixels用于读取帧缓冲区的数据到内存,前四个参数同sgpugl_glCopyPixels,在窗口确认了一块矩形区域。第五个参数表示读取的内容,例如:GL_RGB就会依次读取像素的红、绿、蓝三种数据。第六个参数表示读取的内容保存到内存时所使用的格式,如GL_UNSIGNED_BYTE会把各种数据保存为GLubyte。第七个参数表示一个指针,即从帧缓冲区读取的数据保存在内存的位置。必要时可以使用sgpugl_glPixelStorei设置像素保存到内存时的对齐方式。通过sgpugl_glRasterPos2f设置像素绘制的起始位置(因此该函数比起sgpugl_glReadPixels少了第一个和第二个参数),sgpugl_glDrawPixels将从内存读取数据到帧缓冲区,从起始位置绘制出刚刚窗口上指定的矩形中的内容。该绘制方法比起sgpugl_glCopyPixels速度较慢,因为整个过程是从显存-内存-显存,并且涉及格式转换问题。

(3)sgpugl_glBitmap用于将位图数据绘制在屏幕,位图数据可以理解为0、1矩阵,为1则在屏幕的对应像素绘制、为0不绘制。

image-20230417093822003

​ 第(3)部分代码如下,glBitmap函数参数前两位定义了位图的宽、高,如图字符大小为12*8的方阵,每一行数据用8位16进制表示。注意位图数据总是按块存储,每块的位数总是8的倍数,但实际位图的宽并不一定使8的倍数。组成位图的位从位图的左下角开始画:首先画最底下的一行,然后是这行的上一行,依此类推。第三、四个参数指定了位图的原点,默认为左下角,向上和向右为正方向。第五、六个参数指定该在位图光栅化后光栅位置的增量,如下代码的意思是第二个字符F在第一个字符F的基础上分别向X正轴和Y正轴移动20个像素单位。 (不过试的时候发现glRasterPos2i(0, 0)才显示,glRasterPos2i(20, 20)不显示,所以不太清楚这里的坐标系)

image-20230427202027799

1
2
3
4
5
6
7
8
9
GLubyte rasters[12] = {
0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc,
0xfc, 0xc0, 0xc0, 0xc0, 0xff, 0xff};
glColor3f(1.0, 0.0, 0.0);//必须放RasterPos2i前
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//光栅位置坐标与glVertex()提供的坐标同样对待,位于视口以外的点的当前光栅位置无效
glRasterPos2i(0, 0);
glBitmap(8, 12, 0.0, 0.0, 20.0, 20.0, rasters);
glBitmap(8, 12, 0.0, 0.0, 0.0,0.0, rasters);

​ 不过位图的宽高和实际给的位图数据也可能不同。

image-20230427203459953

13.图元绘制

首先确定绘制的图元类型,注意gpugl_glDrawArrays和sgpugl_glBegin的图元参数不同。

图元类型 gpugl_glDrawArrays是否有该参数 sgpugl_glBegin是否有该参数 流程图中对应类型
GL_POINTS
GL_LINES 线一类
GL_LINE_LOOP 线一类
GL_LINE_STRIP 线一类
GL_TRIANGLES 三角形一类/多边形
GL_TRIANGLE_STRIP 三角形一类/多边形
GL_TRIANGLE_FAN 三角形一类/多边形
GL_POLYGON × 三角形一类/多边形
GL_QUADS × 三角形一类/多边形
GL_QUAD_STRIP × 三角形一类/多边形

如果是绘制点可以用sgpugl_glPointSize(f)设置光栅化时一个点的直径,默认为1。如果绘制线,可以用sgpugl_glLineWidth设置直线宽度,默认为1。

直线可以用点画模式进行绘制。首先通过sgpugl_glEnable开启点画模式。sgpugl_glLineStipple设置点画的方式,其参数是由0、1组成的16位模式序列和重复因子,从模式低位开始,如果模式中的位是1,直线中对应像素就被绘制,否则就不绘制(如果直线长度大于16位则不断重复上述规则)。重复因子用来扩展模式,如模式中如果出现3个1会被扩展成个6个1。如果没有启用点画线功能,模式默认为OxFFFF、重复因子为1。多边形也可以用点画模式进行绘制。首先通过sgpugl_glEnable开启点画模式。sgpugl_glPolygonStipple设置点画的方式,其参数是一个指向3232的位图指针,可以理解为3232的0、1矩阵,通过GLubyte数组自定义(128个2位16进制序列)。从每个序列低位开始,模式中的位是1,多边形中对应像素就被绘制,否则就不绘制(如果多边形像素大于32*32则不断重复上述规则)。

sgpugl_glShadeModel用于设置平滑着色或者恒定着色。注意除了GL_POLYGON以外,恒定着色图元中片元的颜色为图元最后一个顶点的颜色,而GL_POLYGON下图元中片元的颜色为图元第一个顶点的颜色。

设置完上述所有后即可调用绘制函数,注意sgpugl_glDrawElements除了需要顶点数据以外,还需要索引数据。

img

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信