音视频开发-OpenGL图形显示常见问题以及解决办法

渲染过程中可能产生的问题

在绘制3D场景的时候,我们需要决定哪些部分是对观察者 可⻅的,或者哪些部分是对观察者不可⻅的.对于不可见的部分,应该及早丢弃.例如在⼀个不透明的墙壁后,就不应该渲染.这种情况叫做”隐藏面消除”(Hidden surface elimination).

油画算法

  1. 先绘制场景中的离观察者较远的物体,再绘制较近的物体.
  2. 例如下面的图例: 先绘制红色部分,再绘制黄色部分,最后再绘制灰色部分,即可解决隐藏面消除的问题
    在这里插入图片描述
    使用油画算法,只要将场景按照物理距离观察者的距离远近排序,由远及近的绘制即可.那么会出现什么问题? 如果三个三⻆形是叠加的情况,油画算法将⽆法处理。
    在这里插入图片描述

分析

当我们去看一个正方体时,只能最多看到3个面,正方体有6个面,那么其他3个面是看不到的,那么也不需要渲染。所以,我们只需要渲染我们看到的3个面!

那么怎么判断那些面是可见的,哪些面是不可见的呢?即如何告诉OpenGL哪些面是正面。哪些面是反面?
答案就是通过分析顶点数据的顺序

  • 正面:按照逆时针顶点连接顺序的三角形
  • 背面:按照顺时针顶点连接顺序的三角形

在这里插入图片描述
分析:

  • 左侧三⻆形顶点顺序为: 1—> 2—> 3 ; 右侧三角形的顶点顺序为: 1—> 2—> 3
  • 当观察者在右侧时,则右边的三⻆形方向为逆时针⽅向则为正面,⽽左侧的三⻆形为顺时针则为背面
  • 当观察者在左侧时,则左边的三角形为逆时针⽅向判定为正面,⽽右侧的三角形为顺时针判定为背⾯.

正⾯和背面是有三角形的顶点定义顺序和观察者方向共同决定的.随着观察者的⻆度方向的改变,正面背面也会跟着改变。

正背面剔除

从而引入OpenGL的正背面剔除概念

  1. 开启表面剔除(默认为背面剔除)
void glEnable(GL_CULL_FACE);
  1. 关闭表面剔除
void glDisable(GL_CULL_FACE);
  1. 用户选择剔除哪个面(正面/背面)
void glCullFace(GLenum mode);
//model参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK,默认GL_BACK
  1. 用户指定绕序哪个为正面
void glFrontFace(GLenum mode);
//mode参数为:GL_CW,GL_CCW,默认是为GL_CCW

代码举例:

  1. 剔除正面实现
glCullFace(GL_BACK);
glFrontFace(GL_CW);
  1. 剔除正面实现 2
void glCullFace(GL_FRONT);

深度测试

深度

深度其实就是该像素点在3D世界坐标系中,距离摄像机的距离,Z值

深度缓冲区

深度缓冲区,为一块内存区域,专门存储着每个像素点的深度值。深度值(Z值)越大,即像素点离摄像机越远,那当我们没有开启混合(Blend)的时候,两张图片在同一像素点有不同的Z值,那么我们会舍弃Z值较大的,渲染并显示Z值较小的。

为什么要使用深度缓冲区

在不使用深度测试的时候,如果我们先绘制一个距离比较近的物体,再绘制距离较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么重要了. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除⾮调⽤ glDepthMask(GL_FALSE).来禁⽌写入.

深度测试

那么深度测试在这种背景下产生了,他会比较同一个像素点的深度大小,来决定是否显示

深度缓冲区(DepthBuffer)和颜色缓存区(ColorBuffer)是对应的.颜色缓存区存储像素的颜色信息,⽽深度缓冲区存储像素的深度信息. 在决定是否绘制一个物体表面时, ⾸先要将表面对应的像素的深度值与当前深度缓冲区中的值进行比较. 如果大于深度缓冲区中的值,则丢弃这部分.否则 利用这个像素对应的深度值和颜色值.分别更新深度缓冲区和颜色缓存区. 这个过程称为”深度测 试”

深度值的计算

深度值一般由16位,24位或者32位值表示,位数越多越精确。值的范围位于[0,1]之间
但是我们通常设置Z值是不在[0,1]范围内的,那么就需要将Z值转换成深度值,下面的线性方程转换成深度值
在这里插入图片描述
这的far和near参考下图中的观察者坐标系
在这里插入图片描述
但正确投影特性的非线性深度方程是和1/z成正比的,非线性方程和1/z成正比
在这里插入图片描述
当z值越大时,越接近于1,并且当深度精度越大时,越准确
在这里插入图片描述

代码编写

  • 开启深度测试
glEnable(GL_DEPTH_TEST);
  • 在绘制场景前,我们需要清除深度缓冲区
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  • 制定深度测试判断模式
void glDepthFunc(GLEnum mode);

在这里插入图片描述

  • 打开/阻断深度缓冲区写入
void glDepthMask(GLBool value);
value : GL_TURE 开启深度缓冲区写⼊入; GL_FALSE 关闭深度缓冲区写⼊入

ZFlighting闪烁问题

由于深度值是有精度的,当精度不够的时候,OpenGL就不能很好的区分深度值,如下图的重叠部分,深度值很接近,就会出现图片闪烁,上下交替。
在这里插入图片描述
在这里插入图片描述

Polygon Offset ⽅式

启用Polygon Offset

让深度值之间产⽣间隔.如果2个图形之间有间隔,是不是意味着就不会产⽣生干涉.可以理解为在执行深度测试前将⽴方体的深度值做⼀些细微的增加.于是就能将重叠的2个图形深度值之前有所区分.
//启⽤用Polygon Offset ⽅方式

glEnable(GL_POLYGON_OFFSET_FILL)

参数列列表:

GL_POLYGON_OFFSET_POINT 对应光栅化模式: GL_POINT 
GL_POLYGON_OFFSET_LINE 对应光栅化模式: GL_LINE
GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL

指定偏移量

  • 通过glPolygonOffset 来指定.glPolygonOffset 需要2个参数: factor , units 每个Fragment 的深度值都会增加如下所示的偏移量:
    Offset = ( m * factor ) + ( r * units);
  • m : 多边形的深度的斜率的最大值,理解⼀个多边形越是与近裁剪面平⾏,m 就越接近于0.
  • r : 能产⽣于窗口坐标系的深度值中可分辨的差异最小值.r 是由具体是由具体OpenGL 平台指定的一个常量量.
  • 一个⼤于0的Offset 会把模型推到离你(摄像机)更远的位置,相应的一个小于0的Offset 会把模型拉 近
  • ⼀般而言,只需要将-1.0 和 -1 这样简单赋值给glPolygonOffset 基本可以满⾜足需求.

关闭Polygon Offset

glDisable(GL_POLYGON_OFFSET_FILL);

不要将两个物体靠的太近,避免渲染时三角形叠在一起。这种方式要求对场景中物体插入一个少量的偏移,那么就可能避免ZFighting现象。例如上⾯的⽴⽅体和平面问题中,将平⾯下移0.001f就可以解决这个问题。当然⼿动去插⼊这个小的偏移是要付出代价的.

猜你喜欢

转载自blog.csdn.net/shengpeng3344/article/details/91410305