基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十七)深度测试

2023-11-18

Vries的教程是我看过的最好的可编程管线OpenGL教程,没有之一其原地址如下,https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/01%20Depth%20testing/ 关于深度测试的详细知识了解请看原教程,本篇旨在对Vires基于visual studio平台的编程思想与c++代码做纯Qt平台的移植,重在记录自身学习之用)

 

程序源代码链接:https://pan.baidu.com/s/1IiVLsr5PB7FGpuvVL7HVzg   提取码:soln

编译环境:Qt5.9

编译器:MSVC

IDE:QtCreator

 

一.啥是个深度测试

  最简单的莫过于对同一个场景做一个

  glEnable(GL_DEPTH_TEST); //深度测试

  开启与关闭的对比。

开启效果:

关闭效果:

差别在于,深度测试开启后,渲染效果有了空间立体感,各物体可以很明显感知到前后左右的位置关系。

关闭后,一切的渲染效果按照各物体的绘制顺序展现。比如淡蓝色的河流流域是最后绘画的,不管从哪个视角看,河流永远在前面。

所以深度测试与glViewport()视口函数相关。需要确定各个物体的片段谁前谁后的关系。需要根据视角观察位置,记录所有片段与视角位置的深度关系。这个记录工具称为深度缓冲。每绘制一个新物体,就将这个物体片段的深度信息"a"与深度缓冲已存储的深度"b"做对比,如果a<b, 说明a在b的前面,则绘制a,并更新深度缓冲。

所以每一次视角观察位置的变化,都要重新清理一次深度缓冲,

为方便起见,每一帧的渲染都对深度缓冲做一个清理重设,用以下函数

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  

 

二.深度测试函数

  当然我们可以控制深度测试的比较函数,确定什么情况通过测试,只有通过测试的片段才能被渲染。

  我们可以修改这个比较规则,默认规则是当片段的深度值小于深度缓冲中的存储值时,测试通过。即

   glDepthFunc(GL_LESS);

像Vries一样,我们搭一个小场景。两个立方体,一个平面。

选择默认比较函数  

  glDepthFunc(GL_LESS);

场景正常显示。

选择比较函数GL_ALWAYS, 所有片段永远通过深度测试,则展示顺序按程序中片段的绘制顺序来。

  glDepthFunc(GL_ALWAYS);

平面是最后被绘制的,所以永远在前。  

三.深度值精度

  这里解释一下,每个像素的深度缓冲是一个介于0.0到1.0之间的浮点数值,场景中物体每个片段的深度应同样换算至这个范围内,再进行比较。一种简单的换算方法是线性换算

  near与far是投射投影perspective的平截面体中近平面与远平面的位置。

 

  这样,物体的z值与near=1,far=50平截面体的深度是这样的一种关系。

  但实际中,永远不会出现这样的线性插值!!!因为要保证距离摄像头较近范围的精度问题,所以我们要假设深度与1/z成某种正比关系,距离摄像头近的范围,精度大,远的精度小。简单假设一个深度计算公式:

这是一种深度与z值的非线性关系,当z值在1~10之间时,深度值在0~0.9之间,距离较近的地方,深度计算会更加细致精细。这就给了近处的物体很大的深度精度,因为我们没必要给远处的精度与近处一样。

当然,这就会产生一个问题:

  深度缓冲中的0.5并不代表,这个片段的z值在近远平面之间,而是非常接近近平面!!!

四.深度缓冲的可视化

  片段着色器中的内建变量gl_FragCoord.z包含了该片段的深度值,当然是非线性的深度值,近处精度高,远处精度小。

  因为这个变量的值介于0.0~1.0之间,所以我们可以直接将其输出看效果

  新建箱子cube与平面plane的非线性着色器:

  cube_nonLinear.vert

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main(){
  gl_Position = projection * view * model * vec4(aPos, 1.0f);

}

  cube_nonLinear.frag

#version 330 core
out vec4 FragColor;

void main(){
  FragColor = vec4(vec3(gl_FragCoord.z), 1.0);
}

看效果,整个界面基本都是白色的,为什么呢,因为我们假设的perspective的平截面体的near是0.1f, far是100.0f

而非线性深度函数对近平面的精度非常高,且整个场景据视角观察位置非常近,所以绝大多数片段的深度值大于0.9,

而rgb(0.9f, 0.9f, 0.9f)已经非常接近白色了,所以整个场景基本就是白色的。

只有非常接近视角位置的片段深度值才会小,才会接近黑色

  我们在将非线性深度转换为线性深度,观察颜色变化:

cube_linear.vert

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main(){
  gl_Position = projection * view * model * vec4(aPos, 1.0f);

}

cube_linear.frag

#version 330 core
out vec4 FragColor;

uniform float near;
uniform float far;

float LinearizeDepth(float depth){
  float z = depth * 2.0 - 1.0; // back to NDC
  return (2.0 * near * far) / (far + near - z * (far - near));
}

void main(){
  float depth = LinearizeDepth(gl_FragCoord.z) / far; // 为了演示除以 far
  FragColor = vec4(vec3(depth), 1.0);
}

转换公式看这篇文章,我没看懂。。。。。

http://www.songho.ca/opengl/gl_projectionmatrix.html

这次整个场景偏黑色,我们继续分析,整个场景据观察位置较近,就上图所示的观察角度而言,距离大致在0~10之间,而perspective的平截面体是一个近平面为0.1f,远平面为100.0f的几何。所以场景中各片段的深度值在0.0~0.1之间,

而rgb(0.1f, 0.1f, 0.1f)也是一个近黑色的颜色,故整个场景近黑色。

当我们拉远视角,颜色就会变白一些,因为深度值更大了,如下图所示:

 

五.深度冲突

  在最后,Vries还提了一个非常有趣且常见的现象,当我们进入箱子的内部,会发现有明显的锯齿状波纹若隐若现,这是因为箱子的底面与plane平面处于相同位置,OpenGL不知道要显示谁,造成了冲突。这种冲突也会出现在距离观察视角较远的地方,因为远处的片段深度精度较低,更容易出现深度冲突。

有三种方法可以解决深度冲突问题:

1.不要将各物体摆的太近,就上图所示的冲突,将箱子抬起一点点的高度,比如0.0001f,原则上就可以解决冲突问题。

2.因为越靠近perspective的平截面体近平面的片段,深度精度越高,所以可以把近平面设远一些,但这时会出现距离观察位置较近的一些片段无法显示,这个度需要根据需求自己平衡。

3.选用更高精度的深度缓冲,深度缓冲默认是24位的,可以手动选择32位进行深度存储,当然这会造成空间浪费。

最后,仅针对上图问题,因为箱子地面与plane平面是重合的,不是深度精度不够的问题,所以仅第一种解决方法有效。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十七)深度测试 的相关文章

  • Java架构直通车——ThreadLocal实现RabbitMQ消息的批量发送

    文章目录 引入 什么是ThreadLocal 使用ThreadLocal 引入 之前 我们完成了单个消息的发送 以及单个消息发送的多线程池化 这里 我们继续完成批量发送消息的封装 因为rabbitMq本身是不支持批量发消息的 所以我们可以直
  • Ubuntu 20.04安装配置opencv4.2.0及踩坑

    Ubuntu 20 04安装配置opencv4 2 0及踩坑 所需各种文件链接在此 链接 https pan baidu com s 1kf cljseuUbq7MqzBg TQw 提取码 ooh2 一 在安装配置opencv之前如果没换源
  • spacemacs创建layer

    m x configuration layer create layer 选择目录 默认是private 然后命名layername 比如my 在 spacemacs文件里 增加my到layer list里 在private my pack
  • 马踏棋盘算法(骑士周游问题)

    将马随机放在国际象棋的8 8棋盘的某个方格中 马按走棋规则进行移动 要求每个方格只进入一次 走遍棋盘上全部64个方格 代码 include stdafx h include
  • HackPorts – Mac OS X 渗透测试框架与工具

    HackPorts是一个OS X 下的一个渗透框架 HackPorts是一个 超级工程 充分利用现有的代码移植工作 安全专业人员现在可以使用数以百计的渗透工具在Mac系统中 而不需要虚拟机 工具列表 0trace 3proxy Air Au
  • 调用 matlab的function函数出现未定义函数的现象

    调用 matlab的function函数出现未定义函数的现象 将matlab的默认位置 C Users Administrator Desktop 改为当前文件所在位置即可 具体参考 链接 https blog csdn net wzgl
  • 2019-12-28

    c语言 入门级别代码解一元二次方程 其实实现输入a b c解出x的值并不难 首先我们先要了解一元二次方程的解法 将步骤一步步套入程序 include

随机推荐

  • SpringSecurity一日干

    前后端登录校验的逻辑 完整流程 本质就是过滤器链 1 提交用户名和密码 2 将提交的信息封装Authentication对象 3 传给下一个 调用2中的authenticate方法进行验证 4 3步骤也验证不了需要调用3的authentic
  • 【计算机组成原理】总线宽度和总线带宽的区别,总线带宽的计算

    总线宽度 总线的宽度 指总线在单位时间内可以传输的数据总数 即平常说的32位 64位 总线宽度 总线位宽 数据线的根数 总线带宽 总线带宽 指总线在单位时间内可以传输的数据总数 等于总线的宽度与工作频率的乘积 通常单位 MB s MBps
  • vscode修改插件的安装的位置,从c盘转移到其他盘

    作为一个电脑非常落后的人 c盘每MB位置都很珍贵 能安装到别的盘的就尽量安装到其他盘 首先在c盘找到 vscode文件 下面的extensions文件就是插件放置的位置 将extensions里的文件全部剪切到自己定义的位置下 原来的ext
  • Quartz 体系结构

    Quartz的体系结构 Quartz的重要组件 Scheduler 用于与调度程序交互的主程序接口 Scheduler调度程序 任务执行计划表 只有安排进执行计划的任务Job 通过scheduler scheduleJob方法安排进执行计划
  • TFT-LCD显示屏工作原理图文解析

    一直很好奇手机屏幕的显示原理 这是LCD的 OLED 屏幕的与此不同 直接贴上原文链接 http www 58display com article zixun 208 html 以下是复制的原文 液晶显示器是什么 不同的应用环境 有不同的
  • C++-对四个智能指针:shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解

    回答如下 C 的智能指针是一种特殊类型的 指针 其主要目的是自动跟踪内存分配和释放 以避免程序中出现内存泄露或空悬指针等问题 主要采用的技术是 借助于类的生命周期 当超出了类的作用域时 类对象会自动调用析构函数 然后就可以释放内存等资源 无
  • Mac M1安装Homebrew 简单实用

    1 首先创建安装目录 sudo mkdir p opt homebrew 2 将目录属主修改为当前用户 方便直接实用brew install sudo chown R whoami opt homebrew 3 进入 opt文件夹 cd o
  • 第08章 Spring-Boot 使用简介

    第08章 Spring Boot 简介 Spring框架功能很强大 但是就算是一个很简单的项目 我们也要配置很多东西 因此就有了Spring Boot框架 它的作用很简单 就是帮我们自动配置 Spring Boot框架的核心就是自动配置 只
  • 轻量级自动化测试框架WebZ

    一 什么是WebZ WebZ是我用Python写的 关键字驱动 的自动化测试框架 基于WebDriver 设计该框架的初衷是 用自动化测试让测试人员从一些简单却重复的测试中解放出来 之所以用 关键字驱动 模式是因为我觉得这样能让测试人员 测
  • 数据库中索引会失效的几种情况(oracle)

    文章目录 数据库中索引会失效的几种情况 oracle 1 没有 WHERE 子句 2 使用 IS NULL 和 IS NOT NULL 3 WHERE 子句中使用函数 4 使用 LIKE T 进行模糊查询 5 WHERE 子句中使用不等于操
  • 输入两个正整数,输出它们的最大公约数和最小公倍数

    include
  • python 列表元组字典集合相关知识

    python 数据类型 列表 可变数据类型 列表的创建 或者 list 列表的索引 由下标0开始 最后一个为 1 列表的切片 list start end step 列表的计算 支持 等方法 列表的方法 格式 列表名称 方法名字 index
  • 如何结束8080端口的进程

    1 找到8080端口进程 win r 输入cmd打开终端窗口 输入netstat aon findstr 8080 找出所有的进程 2 结束对应的进程 taskkill F PID 53408
  • tinymce 去掉编辑器换行默认增加的p标签

    问题 tinymce 编辑器里面使用回车换行后会自动添加p标签 解决方法 增加forced root block这个属性 替换为空后 换行就没有p标签了 格式 forced root block 删除在tinymce中自动添加的p标签 如下
  • HashMap中为何X % length = X & (length - 1)(求余%和与运算&转换问题)

    目录 一 引出问题 二 结论 三 分析过程 总结 一 引出问题 在前面讲解 HashMap 的源码实现时 有如下几点 初始容量为 1 lt lt 4 也就是24 16 负载因子是0 75 当存入HashMap的元素占比超过整个容量的75 时
  • Pod控制器(一)ReplicaSet

    目录 1 关于Pod控制器 1 1Pod控制器概述 1 2 控制器与Pod对象 1 3 ReplicaSet控制器 1 3 1 ReplicaSet概述 1 3 2 创建ReplicaSet 1 3 3 ReplicaSet管控下的Pod对
  • ajax同步异步的具体事例,Ajax同步和异步(示例代码)

    Ajax在默认情况下是异步执行的 即其属性 async boolean 是否异步 同步和异步的区别 同步 Client 向 Server请求数据 直到该部分数据返回时 Client在请求返回值后的相应程序队列才会按顺序执行 在此期间 Cli
  • 微信小程序 车牌号输入组件

    概述 一个小组件 用于方便用户输入车牌号码 详细 概述 有时候我们开发过程中会遇到需要用户输入车牌号的情况 让客户通过自带键盘输入 体验不好且容易出错 例如车牌号是不能输入O和I的 因此需要有一个自定义的键盘 让客户输入正确的车牌号 详细
  • 基于MyApps低代码平台生成的CRM实现客户的高效管理

    随着市场的发展 客户开始变得越来越重要 因此很多公司开始追求客户数量用尽浑身解数 可盲目发展 一股脑的想要扩大客户数量 也导致企业无法对客户进行有效的管理 不可避免地出现以下问题 1 没有对新客户做好分析 也疏于老客户的管理 导致客户流失的
  • 基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十七)深度测试

    Vries的教程是我看过的最好的可编程管线OpenGL教程 没有之一 其原地址如下 https learnopengl cn github io 04 20Advanced 20OpenGL 01 20Depth 20testing 关于深