android openGL ES详解——深度缓冲区

news2024/11/13 11:32:40

一、深度缓冲区概念

深度缓存区是指一块专门内存区域,存储在显存中,用于存储屏幕上所绘制图形的每个像素点的深度值。深度值越大,离观察者越远。深度值越小,里观察者越近。

深度缓冲区与帧缓冲区相对应,用于记录上面每个像素的深度值,通过深度缓冲区,我们可以进行深度测试,从而确定像素的遮挡关系,保证渲染正确。

二、深度概念

深度,指OpenGL坐标系中,像素点的Z坐标距观察者的距离。其实就是该象素点在3d世界中距离摄象机的距离(绘制坐标),深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!深度值(Z值)越大,则离摄像机越远。

为什么需要深度?

在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。

三、深度缓冲原理

       深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素相关联。将深度值与屏幕上的每个像素点进行一一对应,然后将深度值存储到深度缓冲区。
       首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值(一般是远裁剪面)。然后,在场景中以任意次序绘制所有物体。硬件或者软件所执行的图形计算把每一个绘制表面转换为窗口上一些像素的集合,此时并不考虑是否被其他物体遮挡。

      其次,OpenGL会计算这些表面和观察平面的距离。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把它的深度值和已经存储在这个像素的深度值进行比较。新像素深度值<原先像素深度值,则新像素值会取代原先的;反之,新像素值被遮挡,他颜色值和深度将被丢弃。

      为了启动深度缓冲区,必须先启动它,即glEnable(GL_DEPTH_TEST)。每次绘制场景之前,需要先清除深度缓冲区,即glClear(GL_DEPTH_BUFFER_BIT),然后以任意次序绘制场景中的物体。

四、深度测试

深度测试概念

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

深度值计算

      深度值一般由16位、24位或者32位值表示,通常是24位,位数越高的话,深度的精确度越高,深度值的范围在[0,1]之间,值越小表示月靠近观察者,值越大表示远离观察者。

      深度缓冲主要是通过计算深度值来比较大小,在深度缓冲区中包含深度值,介于0.0~1.0之间,从观察者看到其内容与场景中的所有对象的z值进行了比较,这些视图中的z值可以投影平头截体的近平面和远平面之间的任意值。我们因此需要些方法转换这些视图空间的z值到[0,1]的范围内,下面的线性方程把z值转换为0.0~1.0之间的值。

  • glDepthRange 

深度测试是采用深度缓存器算法,消除场景中的不可见面。在默认情况下,深度缓存中深度值的范围在0.0到1.0之间,这个范围值可以通过函数:

glDepthRange (nearNormDepth, farNormalDepth);

将深度值的范围变为nearNormDepth到farNormalDepth之间。这里nearNormDepth和farNormalDepth可以取0.0到1.0范围内的任意值,甚至可以让nearNormDepth > farNormalDepth。这样,通过glDepthRange函数可以在透视投影有限观察空间中的任意区域进行深度测试。

  • glClearDepth 
glClearDepth (maxDepth);

参数maxDepth可以是0.0到1.0范围内的任意值。glClearDepth用maxDepth对深度缓存进行初始化,而默认情况下,深度缓存用1.0进行初始化。由于在进行深度测试中,大于深度缓存初始值的多边形都不会被绘制,因此glClearDepth函数可以用来加速深度测试处理。这里需要注意的是指定了深度缓存的初始化值之后,应调用: glClear(GL_DEPTH_BUFFER_BIT); 完成深度缓存的初始化。使用深度测试之前需要清除深度缓冲和颜色缓冲。glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  • glDepthFunc

在默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。这种比较测试的方式可以通过函数:

glDepthFunc(func)

其中参数func的值介绍如下表格,其中默认值是GL_LESS。一般来将,使用glDepthFunc(GL_LEQUAL);来表达一般物体之间的遮挡关系。

参数值参数说明
GL_ALWAYS总是通过测试
GL_NEVER总是不通过测试
GL_LESS(默认值)当前深度值 < 存储的深度值,通过测试
GL_LEQUAL当前深度值 <= 存储的深度值,通过测试
GL_GEQUAL当前深度值 >= 存储的深度值,通过测试
GL_GREATER当前深度值 > 存储的深度值,通过测试
GL_EQUAL当前深度值 = 存储的深度值,通过测试
GL_NOTEQUAL当前深度值 != 存储的深度值, 总是通过测试

开启深度测试的方法是

glEnable(GL_DEPTH_TEST);

关闭深度测试的方法是

glDisable(GL_DEPTH_TEST);

所带来的弊端

启用了深度测试,那么这就不适用于同时绘制不透明物体。当需要绘制半透明物体时,需注意,在绘制半透明物体时前,还需要利用glDepthMask(GL_FALSE)将深度缓冲区设置为只读形式,否则可能出现画面错误。为什么呢,因为画透明物体时,将使用混色,这时就不能继续使用深度模式,而是利用混色函数来进行混合。这一来,就可以使用混合函数绘制半透明物体了。

深入

像素的深度值是由视矩阵和投影矩阵决定的。在近裁平面上的像素深度值为0,在远裁平面上的像素的深度值为1。场景中的每个对象都需进行绘制,通常最靠近相机的像素会被保留,这些对象阻挡了在它们后面的对象的可见性。
深度缓冲通常还包含stencil(模板) bits – 所以深度缓冲又被叫做depth-stencil缓冲。深度缓冲总是32 bits,但可以用不同的方式组合,类似于纹理格式。常用的深度格式是Depth32,这种格式中32 bits都用来存储深度信息。另一个常用格式是DepthFormat.Depth24Stencil8,这种格式中24 bits用于深度计算而8 bits用于模版缓冲(stencil buffer)。

五、深度缓冲区案例讲解

如果我们想要在三维空间里画两个正方形:一个红色的,一个绿色的,而且从人眼的观察角度看,绿色正方形在红色正方形的后面,最后看上去应该是这样的:

要点在于,从观察者的角度看,绿色正方形在红色正方形的后面,因此绿色正方形的一部分被红色正方形遮挡。

  然而,在启用深度测试前,正方形的相对位置完全取决于绘制这两个正方形的顺序。如果我们先绘制红色正方形,再绘制绿色正方形,看上去会是这样:

由于绿色正方形是最后绘制的,因此它在屏幕中遮挡了红色正方形,虽然从顶点坐标来看它应该在红色正方形的后面。而如果最后绘制的是红色正方形,看上去就是正确的效果了。

        针对这个问题应该怎么办呢?我们需要你,深度缓冲!

  深度缓冲的原理其实非常简单,它为窗口内的每个像素点记录一个深度信息,当有新的点绘制在窗口的某个位置时,OpenGL会比较这个点的深度与此位置之前保留的深度大小,默认情况下保留深度较小的那个像素点,也就是距离我们的视角更近的那个点。从而,如果新绘制点的深度小于之前保留的深度大小,则在窗口内绘制这个点,并更新深度信息;否则就忽略这个点。

现在,无论我们先绘制哪个,都能得到正确的视觉效果:

      启用深度测试后,对于两正方形像素的重叠部分,由于红色正方形上的点具有更小的深度,因此只有红色部分会被保留。

       有些杠精朋友可能会说:“我就是不喜欢用深度测试,我乐意每次计算哪些物体距离视线更近,越近的物体越到最后才画,你能拿我怎样?” 年轻人不要太气盛,看看下面这个:

如果没有启用深度缓冲,看上去会是这样:

先画红色,再画绿色

或者是这样:

 先画绿色,再画红色

最后的视觉效果都不太对。

六、如何使用深度缓冲?

分为如下三步骤:

1.使用深度缓冲前,需要先启用深度测试,可以通过以下代码来启用深度测试:

glEnable(GL_DEPTH_TEST);

2. 清空深度缓冲

在每一帧渲染开始时,需要清空深度缓冲,以便重新开始存储新的深度信息。可以使用以下代码清空深度缓冲:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

为什么需要清空深度缓冲区?

OpenGL 是一种基于状态机的图形渲染API,它使用颜色缓冲区和深度缓冲区来存储渲染结果。当渲染新的帧时,清空这些缓冲区是必要的,以避免渲染结果受到上一帧渲染结果的影响。

清空深度缓冲区是为了确保在绘制新的帧时,深度测试能够正确地工作。深度测试用于确定哪些像素应该显示在其他像素的前面或后面,以创建透视效果。如果不清空深度缓冲区,旧的深度值可能会影响新的帧的深度测试结果,导致渲染错误。

3.设置深度测试函数

深度测试函数指定OpenGL如何比较新像素的深度信息与深度缓冲中已有的深度信息。可以使用以下代码设置深度测试函数:

glDepthFunc(GL_LESS);//指定深度测试判断模式

深度测试函数glDepthFunc参数值介绍如下:

参数值参数说明
GL_ALWAYS总是通过测试
GL_NEVER总是不通过测试
GL_LESS(默认值)当前深度值 < 存储的深度值,通过测试
GL_LEQUAL当前深度值 <= 存储的深度值,通过测试
GL_GEQUAL当前深度值 >= 存储的深度值,通过测试
GL_GREATER当前深度值 > 存储的深度值,通过测试
GL_EQUAL当前深度值 = 存储的深度值,通过测试
GL_NOTEQUAL当前深度值 != 存储的深度值, 总是通过测试

4.开启/阻断 深度缓冲区的写入

void glDepthMask(GLBool value);
value:
GL_TURE 表示开启深度缓冲区写入
GL_FALSE 表示关闭深度缓冲区写入

七、深度缓冲冲突——Z冲突(Z-Fighting,闪烁问题)

1.ZFighting闪烁的问题

造成ZFighting闪烁问题的原因:是因为开启深度测试后,OpenGL就不会再去绘制模型被遮挡的部分,这样实现显示的更加真实。但是由于深度缓冲区精度的限制对深度相差非常小的情况下,(例如在同一平面上绘制两次深度值一样的图形),openGL就有可能出现不能正确判断两者的深度值得情况,会导致深度测试的结果不可预测,显示出来的现象就是交错闪烁,也就是这两个画面交错出现的情况。如图所示:

其问题产生的主要原因是由于图形靠的太近,导致无法区分出图层先后次序,针对该问题,OpenGL提供了一种多边形偏移(Polygon Offset)方案。

2、ZFighting闪烁问题的问题解决

第一步:启用Polygon Offset(多边形偏移)

解决方法:让深度值之间产生间隔。如果两个图形之间有间隔,是不是就意味着不会产生干涉,可以理解为在执行深度测试前将立方体的深度值做一些细微的调整,于是就能将两个图形的深度值之间有所区分。

启用代码

glEnable(GL_POLYGON_OFFSET_FULL)

参数列表:

GL_POLYGON_OFFSET_POINT 对应点光栅化模式:GL_POINT 

GL_POLYGON_OFFSET_LINE 对应线光栅化模式:GL_LINE

GL_POLYGON_OFFSET_FILL对应像素光栅化模式:GL_FILL

第二步:指定偏移量

1、通过glPolygonOffset来指定,glPolygonOffset需要两个参数,factor,units

2、每个Fragment的深度值都会增加如下所示的偏移量

Offset = ( m * factor ) + ( r * units);

m : 多边形的深度的斜率的最大值,理解一个多边形越是与近裁剪面平行,m 就越接近于0.

r : 能产生于窗口坐标系的深度值中可分辨的差异最小值.r 是由具体OpenGL 平台指定的一个常量。

3、一个大于0的Offset会把模型推到离观察者更远的位置,相应的一个小于0的Offset会把模型拉近。

4、一般而言,只需要将-1.0和-1这样简单的值赋值给glPolygonOffset 基本可以满⾜足需求.

void glPolygonOffset(Glfloat factor,Glfloat units);

应⽤用到⽚段上总偏移计算⽅程式:

Depth Offset = (DZ * factor) + (r * units); DZ:深度值(Z值)

r:使得深度缓冲区产生变化的最小值

负值,将使得z值距离我们更近,⽽正值,将使得z值距离我们更远,我们一般设置factor和units为-1,-1。

第三步、关闭Polygon Offset

glDisable(GL_POLYGON_OFFSET_FILL)

具体代码如下:

//1. 在绘制前,开启多边形偏移
glEnable(GL_POLYGON_OFFSET_FILL)
//2. 指定偏移量,参数一般填 -1 和 -1
glPolygonOffset(-1.0,-1.0);
//3. 绘制完成后关闭多边形偏移
glDisable(GL_POLYGON_OFFSET_FILL)

3、ZFighting闪烁问题的预防

讲了这么久,多边形偏移只是一个解决深度测试隐患的方案,当然能从根源上预防ZFighting闪烁问题才是我们开发中需要注意的。下面是预防ZFighting闪烁问题的三个方案

1、避免两个物体靠的太近:在绘制时,插入一个小偏移

不要将两个物体靠的太近,避免渲染时三角形叠在一起。这种方式要求对场景中物体插入一个少量的偏移比如(将其中一个平面向下偏移0.001f),那么就可能避免ZFighting现象。

2、将近裁剪面(设置透视投影时设置)设置的离观察者远一些:提高裁剪范围内的精确度

尽可能将近的裁剪面设置得离观察者远一些。离近裁剪平面越近,深度的精确度会越高 ,因此尽可能让近裁剪⾯远一些,那么整个裁剪范围内的精确度就会变高。但是这种⽅式会使离观察者较近的物体被裁减掉,因此需要调试好裁剪面参数。 

3、使用更高位数的深度缓冲区:提高深度缓冲区的精确度

尽量使用更高位数的深度缓冲区,通常使用的深度缓冲区是24位的,现在有一些硬件使用32位的缓冲 区,使精确度得到提高。


 

参考文章

深度缓冲详解(DepthBuffer)_depth buffer-CSDN博客

https://www.cnblogs.com/overxus/p/17898609.html

https://www.cnblogs.com/errorman/p/17222794.html

7、OpenGL初探之OpenGL图像渲染的深度缓冲区理解及隐患解决 - 简书

OpenGL_正面剔除,深度测试和Z冲突 - 简书

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2079419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 进程 | 进程优先级进程的环境变量

文章目录 进程概念4、进程优先级4.1基本概念4.2查看系统进程4.2.1 ps -l4.2.2 PRI & NI 4.3用top命令更改已存在进程的nice&#xff1a; 5、环境变量5.1常见环境变量5.2查看环境变量5.3测试PATH配置环境变量 5.4代码中获取环境变量5.4代码中获取环境变量 进程概念 4、进程…

Linux网络:TCP UDP socket

Linux网络&#xff1a;TCP & UDP socket socket 套接字sockaddr网络字节序IP地址转换bzero UDP socketsocketbindrecvfromsendto TCP socketsocketbindlistenconnectacceptsendrecv 本博客讲解 Linux 下的 TCP 和 UDP 套接字编程。无论是创建套接字、绑定地址&#xff0c;还…

软件设计师全套备考系列文章16 -- 程序设计语言基础知识

软考-- 软件设计师&#xff08;16&#xff09;-- 程序设计语言基础知识 文章目录 软考-- 软件设计师&#xff08;16&#xff09;-- 程序设计语言基础知识前言一、章节考点二、基本概念三、文法四、有限自动机五、前缀、中缀、后缀表达式六、传值和引用(传址)七、各个程序语言的…

链表OJ题——使用栈实现单链表的逆序打印

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 题目描述&#xff1a;使用栈&#xff0c;实现单链表的逆序打印 二、解题思路 三、解题代码 /*** 非递归实现单链表的顶逆序打印——>通过栈来实现* param*/public void printReverseListFromStack(){Stack<…

HAL库:GPIO唤醒模式 唤醒睡眠模式下的单片机

目录 HAL库&#xff1a;GPIO唤醒模式 唤醒睡眠模式下的单片机 注意事项&#xff1a; 初始化部分&#xff1a; 主函数测试部分 结果如图 HAL库&#xff1a;GPIO唤醒模式 唤醒睡眠模式下的单片机 注意事项&#xff1a; HAL库滴答定时器默认为打开状态&#xff0c;需要关闭…

AI学习指南深度学习篇:循环神经网络(RNN)Python实践

引言 在人工智能的广袤领域中,循环神经网络(Recurrent Neural Networks, RNNs)因其在处理序列数据中的卓越表现而广受关注。RNN的独特之处在于它能够保留输入数据的历史信息,并利用这些信息来预测后续的输出,这使得它在自然语言处理、时间序列预测等领域中拥有广泛的应用…

新审视零阶优化在内存高效大模型微调中的应用

人工智能咨询培训老师叶梓 转载标明出处 随着大模型模型规模的增大&#xff0c;反向传播&#xff08;BP&#xff09;所需的内存开销也日益增加&#xff0c;这对内存效率提出了挑战。尤其是在设备上训练等内存效率至关重要的应用场景中&#xff0c;解决这一问题变得尤为迫切。 …

Sora 代码规范之Refactor this method to not always return the same value.(目的性问题)

Sora描述 Refactor this method to not always return the same value.&#xff08;目的性问题&#xff09; 上述代码&#xff0c;可能出现 总是返回 null 的情况 解决一下 原因&#xff1a;为什么要这么写呢&#xff1f;因为 下面的代码会对 materialInfos 这个集合处理&#…

房子公摊要消失了?

文&#xff5c;琥珀食酒社 作者 | 璇子 你敢信 才短短三个月 江苏、浙江、广东 这三房价高昂的城市 陆续宣布房市重大改革信号 比如将空中花园绿化阳台、 小区景观绿化亭廊 、 开放式风雨连廊等不计容积率 挑高客厅只算单层面积 甚至部分区域买房面积 直接按照套内面…

足球联赛|基于SprinBoot+vue的足球联赛管理系统(源码+数据库+文档)

足球联赛管理系统 目录 基于SprinBootvue的足球联赛管理系统 一、前言 二、系统设计 三、系统功能设计 5.1 系统前台功能实现 5.2 后台功能模块实现 5.2.1 管理员模块实现 5.2.2 用户后台模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

20+岁老牌定制家居品牌,如何靠呼叫中心捕获年轻消费者?

Home Tour类一镜到底的短视频&#xff0c;在社交平台一直备受年轻人喜爱。金牌厨柜这个20多岁的老牌定制家居品牌&#xff0c;不仅在高端厨柜和定制家居领域有着深厚的积累&#xff0c;而且一直也在探索数字化转型的新路径&#xff1a;用全新的数字化体系&#xff0c;迎合年轻一…

使用VScode的Git版本控制功能(图文版)

☁️ 前言 今天让我来手把手教你简单入门VScode自带的Git版本控制。 &#x1f389; 初始化仓库 初始化仓库之后&#xff0c;仓库里的文件发生了任何改动都会有相应的提示&#xff0c;这对于我们开发和维护项目非常有帮助。 &#x1f389;提交更改 初始化仓库之后&#xff…

机器学习:K-means算法及代码实现

1、K-means算法原理 K-means算法是一种常用的聚类算法&#xff0c;其目的是将数据集划分为K个簇&#xff08;clusters&#xff09;&#xff0c;使得每个簇内部的数据点尽可能相似&#xff0c;而簇与簇之间的数据点尽可能不同。以下是K-means算法的基本原理和步骤&#xff1a; 初…

【C++】C++中的字符串

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream>using namespace std; void total(string str); int main() {string str;cout << "请输入一个字符串:" ;//cin >>str;getline(…

SAP怎么查找系统全部的增强点呢?

1.在已有的BADI查找程序里面有点手无足措的样子&#xff0c;不知道该如何去找增强&#xff01; 2.这个时候刚刚接触系统还不熟悉&#xff0c;系统里面存在了什么增强&#xff0c;这个时候咋办捏&#xff1f;SE38 -SNIF 此时全部的增强点都在这里面啦&#xff01;&#xff01;&…

使用LinkedHashMap实现固定大小的LRU缓存

使用LinkedHashMap实现固定大小的LRU缓存 1. 什么是LRU&#xff1f; LRU是"Least Recently Used"的缩写&#xff0c;意为"最近最少使用"。LRU缓存是一种常用的缓存淘汰算法&#xff0c;它的核心思想是&#xff1a;当缓存满时&#xff0c;优先淘汰最近最少…

二、设置地图配置表

一、导入一个背景图 由于背景图比较大&#xff0c;需要缩小至0.73 二、写配置文件&#xff08;SO&#xff09; 使用List需要一个命名空间 写一个类&#xff0c;声明房间的出现数量和种类&#xff1b;将它实例化出来 三、枚举变量的多选 在枚举变量中标记命名空间&#xff…

C++:list篇

前言: 观看C的list前需要对链表有一些了解&#xff0c;如C语言的链表结构。本片仅介绍list容器中常用的接口函数概念以及使用。 list的概念&#xff1a; 简而言之&#xff0c;C的list是一个双向带哨兵位的链表容器模板 list的构造&#xff1a; 1.list():默认构造 2.li…

2024最新版Python+Pycharm安装教程,安装、环境配置、汉化全搞定,保姆级教学!

一、Python下载 为了节约时间&#xff0c;我将PythonPycharm安装包、集火码全部打包上传至CSDN官方&#xff0c;可放心下载&#xff0c;完全免费&#xff01;&#xff08;安装包均为最新版本&#xff09; 二、Python安装 1.双击运行本地文件夹下的python安装包&#xff08;以…

c++关于字符串的联系

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream> #include<string> using namespace std;int main() {string s1;int letter0,digit0,space0,other0;cout<<"请输入一个字符串:"…