1.正背面剔除
void SetupRC(){
//1.设置背景颜色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
//2.初始化着色器管理器
shaderManager.InitializeStockShaders();
//3.将相机向后移动7个单元:肉眼到物体之间的距离
viewFrame.MoveForward(7.0);
//4.创建一个甜甜圈
//void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
//参数1:GLTriangleBatch 容器帮助类
//参数2:外边缘半径
//参数3:内边缘半径
//参数4、5:主半径和从半径的细分单元数量
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
//5.点的大小(方便点填充时,肉眼观察)
glPointSize(4.0f);
}
//渲染场景
void RenderScene(){
//1.清除窗口和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2.把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//3.设置绘图颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//4.
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
// shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
//使用默认光源着色器
//通过光源、阴影效果跟提现立体效果
//参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
//参数2:模型视图矩阵
//参数3:投影矩阵
//参数4:基本颜色值
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//5.绘制
torusBatch.Draw();
//6.出栈 绘制完成恢复
modelViewMatix.PopMatrix();
//7.交换缓存区
glutSwapBuffers();
}
通过以上代码绘制一个甜甜圈,可以正常显示,但当旋转时,会出现如下情况
隐藏⾯面消除
在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见的,哪些部分是对观察者不可见的.对于不可见的 部分,应该及早丢弃.例如在一个不透明的墙壁后,就不应该渲染.这种情况叫做”隐藏⾯面消除”(Hidden surface elimination).
解决方案:
油画算法
先绘制场景中的离观察者较远的物体,再绘制较近的物体(具体步骤见上图,先绘制红色方块,接着是黄色圆形,接着是灰色长方形),即可解决隐藏面消除的问题;
油画算法弊端
使⽤油画算法,只要将场景按照物理距离观察者的距离远近排序,由远及近的绘制即可.那么会出现什么问题? 如果个三⻆形是叠加的情况,油画算法将⽆法处理
OpenGL提供了正背面剔除(Face Culling)来解决这个问题
正背面剔除(Face Culling)
当我们看一个3D图形时,无论从哪个方向去观察,最多可以看见3个面;那么就没有必要绘制根本看不到的另外3个面,将看不到的3个面以某种方式丢弃,OpenGL的渲染性能即可提高50%
OpenGL 可以做到检查所有正面朝向观察者的面,并渲染它们.从而丢弃背⾯朝向的面. 这样可以节约片元着⾊器的性能
那么OpenGL是如何识别正面背面?答案是分析顶点数据的顺序
背面: 按照顺时针顶点连接顺序的三⻆形面
正⾯: 按照逆时针顶点连接顺序的三⻆形⾯
立体图的正背面
1、左侧三角形顶点顺序为: 1—> 2—> 3 ; 右侧三角形的顶点顺序为: 1—> 2—> 3 .
2、当观察者在右侧时,则右边的三⻆形⽅向为逆时针⽅向则为正面,而左侧的三⻆形为顺时针则为背面
3、当观察者在左侧时,则左边的三⻆形为逆时针⽅向判定为正面,而右侧的三⻆角形为顺时针判定为背面.
正面和背面是有三角形的顶点定义顺序和观察者⽅向共同决定的.随着观察者的⻆度方向的改变,正面背面也会跟着改变
glEnable(GL_CULL_FACE);//开启正面剔除
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);
使用正背面剔除效果后,在旋转的过程中没有黑色阴影出现,但是当旋转到前后重叠的时候,出现了一个缺口
旋转到前后两部分重叠时,正常情况下应该显示的是离我们近的位置即前面部分,后面部分是隐藏面,但是OpenGL中并不能区分两个图层谁显示在前,谁显示在后,由此导致甜甜圈产生了缺口,要解决这个问题,我们要了解深度
2.深度测试
深度
深度其实就是该像素点在3D世界中距离摄像机的距离,Z值
深度值一般由16位,24位或者32位值表示,通常是24位。位数越高的话,深度的精确度越好。深度值的范围在[0,1]之间
值越⼩表示越靠近观察者 值越大表示远离观察者
深度缓存区
深度缓存区,就是一块内存区域,专门存储着每个像素点(绘制在屏幕上的)深度值.存储在显存中
深度值(Z值)越⼤, 则离摄像机就越远. 深度值(Z值)越小, 则离摄像机就越近.
为什么需要深度缓冲区?
在不使用深度测试的时候,如果我们先绘制⼀个距离⽐较近的物体,再绘制距离较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么重要了. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除非调⽤用glDepthMask(GL_FALSE).来禁止写入.
深度测试
深度缓冲区(DepthBuffer)和颜⾊缓存区(ColorBuffer)是对应的.颜色缓存区存储像素的颜色信息,而深度缓冲区存储像素的深度信息. 在决定是否绘制⼀个物体表面时, ⾸先要将表面对应的像素的深度值与当前深度缓冲区中的值进行⽐较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利用这个像素对应的深度值和颜色值.分别更新深度缓冲区和颜色缓存区. 这个过程称为”深度测试”
开启深度测试
glEnable(GL_DEPTH_TEST);
在绘制场景前,清除颜⾊缓存区,深度缓冲
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清除深度缓冲区默认值为1.0,表示最大的深度值,深度值的范围为(0,1)之间.
值越小表示越靠近观察者,值越大表示 越远离观察者
开启深度测试后,上面的缺口问题就解决了,我们也可以开启深度测试来解决隐藏面消除额问题
<br />指定深度测试判断模式
void glDepthFunc(GLEnum mode);
枚举值如下图,一般默认就行,很少修改
打开/阻断 深度缓存区写⼊入
void glDepthMask(GLBool value);
value : GL_TURE 开启深度缓冲区写入; GL_FALSE 关闭深度缓冲区写⼊
Z-Fighting(Z冲突,闪烁)问题
开启深度测试后,由于深度缓冲区精度有限制,导致深度值在误差极小时,OpenGL出现无法判断的情况,导致出现画面交错闪现的现象
其问题产生的主要原因是由于图形靠的太近,导致无法区分出图层先后次序,针对该问题,OpenGL提供了一种多边形偏移(Polygon Offset)方案
多边形偏移(Polygon Offset)
让深度值之间产生间隔.如果2个图形之间有间隔,是不是意味着就不会产生干涉.可以理解为在执行深度测试前将⽴⽅体的深度值做一些细微的增加.
在绘制前,开启多边形偏移
glEnable(GL_POLYGON_OFFSET_FILL)
多边形偏移枚举值 对应的图像填充模式
GL_POLYGON_OFFSET_POINT GL_POINT
GL_POLYGON_OFFSET_LINE GL_LINE
GL_POLYGON_OFFSET_FILL GL_FILL
指定偏移量glPolygonOffset(GLfloat factor, GLfloat units);,参数一般填 -1 和 -1
在绘制完成后,关闭多边形偏移
glDisable(GL_POLYGON_OFFSET_FILL)
预防ZFighting闪烁
- 避免两个物体靠的太近:在绘制时,插入一个小量的偏移
- 将近裁剪面(设置透视投影时设置)设置的离观察者远一些:提高裁剪范围内的精确度
使用更高位数的深度缓冲区:提高深度缓冲区的精确度
<br />
3.颜色混合
当开启深度测试后,2个重叠的图层中,如果上层图层是半透明的,此时就不能通过深度值比较,来进行颜色值的覆盖,而是需要将两个颜色进行混合,然后存入颜色缓冲区。
组合颜⾊
目标颜⾊:已经存储在颜色缓存区的颜色值(例如:前女友)
源颜色:作为当前渲染命令结果进入颜色缓存区的颜色值(例如:现女友)
当混合功能被启动时,源颜色和目标颜色的组合⽅式是混合方程式控制的。
在默认情况 下,混合方程式如下所示:
Cf = (Cs * S) + (Cd * D)
Cf :最终计算参数的颜色
Cs : 源颜色
Cd :目标颜色
S:源合因子
D:目标混合因⼦
设置混合因子
glBlendFunc(GLenum S,GLenum D);
S:源混合因子
D:目标混合因⼦

表中R、G、B、A 分别代表 红、绿、蓝、alpha。
表中下标S、D,分别代表源、目标
表中C 代表常量颜色(默认黑色)
常用混合因子
我们一般用这个就行了
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
改变组合方程式
默认混合方程式: Cf = (Cs*S)+(Cd*D)
实际上远不止这一种混合方程式,可以从5个不同的方程式中进⾏选择
选择混合⽅方程式的函数:
glbBlendEquation(GLenum mode);
<br />
glBlendFunc 灵活设置混合因⼦
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);
strRGB: 源颜色的混合因子
dstRGB: 目标颜色的混合因子
strAlpha: 源颜色的Alpha因子
dstAlpha: 目标颜色的Alpha因⼦
在混合因⼦子表中,
GL_CONSTANT_COLOR,
GL_ONE_MINUS_CONSTANT_COLOR,
GL_CONSTANT_ALPHA,
GL_ONE_MINUS_CONSTANT
值允许混合方程式中引入一个常量混合颜⾊
常量混合颜色,默认初始化为黑色(0.0f,0.0f,0.0f,1.0f),
但是可以修改这个常量混合颜色。
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );