1. 渲染过程中产生的问题
我们绘制的一个由很多三角形组成的实体对象,第一个绘制的三角形可能会被后面绘制的三角形覆盖
,这种就是隐藏面消除问题
。由点和线组成的实体对象也会有这种问题,不过不太明显而已。
2. 油画法解决
对这些三角形进行排序,优先渲染那些较远的三角形,这种就是油画法
。比如下图先绘制红色,再绘制黄色,再绘制灰色。
但是这种方法效率比较低,原因有2个,一个是任何发生几何图形重叠的地方都要进行2次
写操作
,存储写操作会使速度变慢
,二是对独立的三角形进行排序的开销太高
。像下面这种无法区分远近的图油画法无法处理。3. 正面和背面剔除
背面剔除能极大的提高性能
,我们无法看到的面不会再进行绘制。在渲染的图元装配阶段就整体抛弃了一些三角形,没有任何不恰当的光栅化操作。通过分析顶点数据的顺序,按照逆时针顶点链接顺序
的为正面,顺时针链接顺序
的为背面。
// 开启表面剔除(默认背面剔除)
void glEnable(GL_CULL_FACE);
// 关闭表面剔除(默认背面剔除)
void glDisable(GL_CULL_FACE);
/*
* 用户选择剔除那个面(正⾯/背面)
* mode : GL_FRONT(正⾯),GL_BACK(背面),GL_FRONT_AND_BACK (一起),默认GL_BACK
*/
void glCullFace(GLenum mode);
/*
* 用户指定绕序那个为正面
* mode: GL_CW(顺时针),GL_CCW(逆时针),默认值:GL_CCW
*/
void glFrontFace(GLenum mode);
例如:开启背面剔除
void SetupRC(){
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
转动一下会发现被咬了一口,这个需要开启深度测试解决。
4. 深度测试
深度测试是另一种高效消除隐藏表面的技术。在绘制一个像素的时候,将一个Z值分配给它,这个值表示它到观察者的距离。当一个像素需要在屏幕的同样位置进行绘制时,新像素的Z值会与已存储的像素的Z值进行比较,Z值较大的代表距离观察者更近,这样就会覆盖原来的像素。
深度值一般由16位,24位或者32位值表示,通常是24位。位数越高的话,深度的精确度越好。深度值的范围在[0,1]之间,值越小表示越靠近观察者,值越大表示远离观察者。
//开启深度测试
glEnable(GL_DEPTH_TEST);
//关闭深度测试
glDisable(GL_DEPTH_TEST);
//禁止深度缓存区写入
void glDepthMask(GLBool value);
value : GL_TURE 开启深度缓冲区写入; GL_FALSE 关闭深度缓冲区写入
//指定深度测试判断模式
void glDepthFunc(GLEnum mode);
5. ZFighting闪烁问题
开启深度测试后,OpenGL就不会去绘制被遮挡的部分,但是由于深度缓冲区精度的限制,对深度相差非常小的情况下,会出现OpenGL不能准备判断两者的深度值,会导致深度测试的结果不不可预测,显示出来的现象时交错闪烁,前⾯2个画⾯交错出现。
解决方法:
启⽤ Polygon Offset 方式,让深度值产生间隔。
glEnable(GL_POLYGON_OFFSET_FILL)
指定偏移量
void glPolygonOffset(Glfloat factor,Glfloat units);
应⽤到⽚段上总偏移计算方程式:
Depth Offset = (DZ * factor) + (r * units); DZ:深度值(Z值)
r:使得深度缓冲区产生变化的最小值
- 关闭Polygon Offset
glDisable(GL_POLYGON_OFFSET_FILL)
6. 裁剪
在OpenGL 中提高渲染的一种⽅方式,只刷新屏幕上发生变化的部分。OpenGL 允许将要进行渲染的窗口只去指定一个裁剪框。
基本原理:⽤于渲染时限制绘制区域,通过此技术可以再屏幕(帧缓冲)指定一个矩形区域。启⽤剪裁测试之后,不在此矩形区域内的片元被丢弃,只有在此矩形区域内的片元才有可能进⼊帧缓冲。因此实际达到的效果就是在屏幕上开辟了一个小窗口,可以再其中进行指定内容的绘制。
//1 开启裁剪测试
glEnable(GL_SCISSOR_TEST);
//2.关闭裁剪测试
glDisable(GL_SCISSOR_TEST);
//3.指定裁剪窗⼝口
void glScissor(Glint x,Glint y,GLSize width,GLSize height);
x,y:指定裁剪框左下⻆角位置;
width , height:指定裁剪尺⼨
7. 混合
我们把OpenGL 渲染时会把颜⾊值存在颜色缓存区中,每个⽚段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜色将简单的覆盖原来颜⾊缓存区存在的颜色值,当深度缓冲区再次打开时,新的颜⾊片段只是当它们⽐原来的值更接近邻近的裁剪平⾯才会替换原来的颜⾊片段。如果打开了OpenGL的混合功能,那么下层的颜色值不会被清除。
glEnable(GL_BlEND);
7.1 组合颜色:
⽬标颜色:已经存储在颜⾊缓存区的颜色值
源颜色:作为当前渲染命令结果进入颜色缓存区的颜色值
当混合功能被启动时,源颜色和⽬标颜⾊的组合方式是混合方程式控制的。在默认情况下,混合方程式如下所示:
Cf = (Cs * S) + (Cd * D)
Cf :最终计算参数的颜⾊ Cs : 源颜色
Cd :⽬标颜色 S:源混合因子 D:⽬标混合因子
设置混合因子,需要用到glBlendFun函数
glBlendFunc(GLenum S,GLenum D);
S:源混合因⼦子
D:⽬目标混合因⼦子
表中R、G、B、A 分别代表 红、绿、蓝、alpha。
表中下标S、D,分别代表源、目标。
表中C 代表常量颜⾊(默认⿊色)。
7.2 改变混合方程式
- 默认混合⽅程式:
Cf = (Cs*S)+(Cd*D)
- 实际上远不止这⼀种混合方程式,我们可以从5个不同的方程式中进行选择
混合⽅程式的函数:glbBlendEquation(GLenum mode);
- 除了能使⽤glBlendFunc 来设置混合因⼦,还可以有更灵活的选择。
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);
srcRGB: 源颜色的混合因⼦
dstRGB: ⽬标颜⾊的混合因子
srcAlpha: 源颜色的Alpha因子
dstAlpha: ⽬标颜⾊的Alpha因⼦
- glBlendFunc 指定源和目标 RGBA值的混合函数;
- 但是glBlendFuncSeparate函数则允许为RGB 和 Alpha 成分单独指定混合函数。
- _ONE_MINUS_CONSTANT值允许混合方程式中引⼊一个常量混合颜色。
- 常量混合颜⾊默认初始化为黑色(0.0f,0.0f,0.0f,0.0f),但是还是可以修改这个常量混合颜色。
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );