渲染中出现的问题
我们通过默认光源着色器绘制圆环后,旋转圆环发现出现黑色部分,黑色相当于光源无法照射到的面即背面,也就是说在旋转过程中,OpenGL将背面的三角形覆盖到正面的三角形之上.
解决办法
1.油画算法
如上图 油画算法会先对三角形进行排序,先绘制较远的三角形,然后在绘制较近的,这样我们不需要看的部分三角形面就会被需要我们看的图形所遮挡,上面圆环旋转绘制中背面出现的问题也可以得到解决
局限性 :
- 如上图 几个三角形交叉,计算机无法计算三角形远近
- 效率低下,重叠部分像素需要进行多次改变,三角形排序开销过高
- 不推荐使用
2.正背面剔除
换一个思路,如果计算机可以直接知道哪一个三角形在正面,背面的直接不绘制岂不是美哉,答案是当然可以了,在OpenGL中我们通过判断三角形的顶点链接方向来确定它在正面还是背面
- 正⾯: 按照逆时针顶点连接顺序的三⻆形⾯
-
背⾯: 按照顺时针顶点连接顺序的三⻆形⾯
结合观察者的位置综合判断,如上图中的观察者移动到左侧时,左边的三角形在观察者眼中又变成逆时针链接即为正面.当然这是默认的规则,开发者也可以随意更改,但是要注意的是OpenGL是一个状态机当你在一个地方更改了规则后可能会对其他地方造成影响,在不适用的时候要记得恢复,上代码
// 开启表⾯剔除(默认背⾯剔除)
void glEnable(GL_CULL_FACE);
//关闭表⾯剔除(默认背⾯剔除)
void glDisable(GL_CULL_FACE);
// ⽤户选择剔除那个⾯(正⾯/背⾯)
void glCullFace(GLenum mode);
//mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默认GL_BACK
//⽤户指定绕序那个为正⾯
void glFrontFace(GLenum mode);
//mode参数为: GL_CW,GL_CCW,默认值:GL_CCW
//例如,剔除正⾯实现(1)
glCullFace(GL_BACK);
glFrontFace(GL_CW);
// 例如,剔除正⾯实现(2)
glCullFace(GL_FRONT);
在我们开启背面剔除后旋转圆环发现黑色部分已经消失了但是又出现新的问题如下图
3.深度测试
上面的问题是因为在旋转到如上图位置时,其实在计算机看来是拥有多个正面的,计算机无法确定应该显示哪个正面,所以我们引入了深度的概念
- 深度
深度就是在openGL坐标系中,像素点的 Z 坐标距离观察者的距离也可以说像素点3D世界中距离摄像机的距离 - 深度缓冲区(DepthBuffer)
深度缓存区,就是⼀块内存区域,专⻔存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z值)越⼤,则离摄像机就越远
在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物理,再绘制距离较远的物理,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么重要了. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除⾮调⽤glDepthMask(GL_FALSE).来禁⽌写⼊
-深度测试
深度缓冲区(DepthBuffer)和颜⾊缓存区(ColorBuffer)是对应的.颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息. 在决定是否绘制⼀个物体表⾯时, ⾸先要将表⾯对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利⽤这个像素对应的深度值和颜⾊值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试”
//申请一个颜色缓冲区和一个深度缓冲区
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//要启用深度测试
glEnable(GL_DEPTH_TEST);
//关闭深度测试
glDisable(GL_DEPTH_TEST);
//在绘制场景前,清除颜色缓冲区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//指定深度测试判断模式
void glDepthFunc(GLEnum mode);
-
清除深度缓冲区默认值为1.0,表示最⼤的深度值,深度值的范围为(0,1)之间. 值越⼩表示越靠近观察者,值越⼤表示越远离观察者,但当深度比较接近(例如在同⼀平⾯上进⾏2次绘制),当前精度无法确定深度值大小时,就会出现新的问题(真是没完没了)
z-fighting(z冲突、闪烁)
其表现为部分重叠部分不停闪烁,无法确定当前像素点颜色 ,解决办法:
- 启⽤ Polygon Offset,让深度值之间产生间隔,如果两个图形之间有间隔,就可以在深度测试之前将其深度值做增加并区分开来
//启⽤Polygon Offset ⽅式
//参数列表:
//GL_POLYGON_OFFSET_POINT 对应光栅化模式: GL_POINT
//GL_POLYGON_OFFSET_LINE 对应光栅化模式: GL_LINE
//GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL
glEnable(GL_POLYGON_OFFSET_FILL)
- 使用更高位数的深度缓冲区,通常使用的深度缓冲区是24位的,现在有一些硬件使用使用32位的缓冲区,使精确度得到提高