光栅化渲染:可见性问题和深度缓冲区算法

在前面第二章中,我们了解到,在投影点(屏幕空间中的点)的第三个坐标中,我们存储原始顶点 z 坐标(相机空间中点的 z 坐标):

当一个像素与多个三角形重叠时,查找三角形表面上一点的 z 坐标非常有用。 我们找到 z 坐标的方法是使用我们在上一章中学到的重心坐标对原始顶点 z 坐标进行插值。 换句话说,我们可以将三角形顶点的 z 坐标视为任何其他顶点属性,并以与上一章中插值颜色相同的方式对它们进行插值。 在详细研究如何计算 z 坐标之前,让我们先解释一下为什么需要这样做。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎

1、深度缓冲或 Z 缓冲算法和隐藏面剔除

当一个像素与一个点重叠时,我们通过该像素看到的是三角形表面上的一小块区域,为了简化,我们将其简化为单个点(在图 1 中表示为 P):

图1:当一个像素与一个三角形重叠时,这个像素对应于三角形表面上的一个点(图中记为P)

因此,覆盖三角形的每个像素对应于该三角形表面上的一个点。 当然,如果一个像素覆盖多个三角形,那么我们就有几个这样的点。 发生这种情况时的问题是找到这些点中哪一个是可见的。 我们在图 2 中以 2D 形式说明了这个概念:

图 2:当一个像素与多个三角形重叠时,我们可以使用三角形 z 坐标上的点来找到这些三角形中哪一个距离相机最近

我们可以从后到前测试三角形(此技术需要首先通过减小深度对三角形进行排序),但当三角形彼此相交时,这并不总是有效(图 2,底部)。 唯一可靠的解决方案是计算像素重叠的每个三角形的深度,然后比较这些深度值以找出哪一个最接近相机。

如果查看图 2,你可以看到图像中的一个像素与 P1 和 P2 中的两个三角形重叠。 然而,P1 z 坐标 (Z1) 低于 P2 z 坐标 (Z2),因此我们可以推断 P1 在 P2 之前。 请注意,需要此技术,因为三角形是按“随机”顺序测试的。 正如之前提到的,我们可以按深度递减的顺序对三角形进行排序,但这还不够好。 一般来说,它们只是按照程序中指定的顺序进行测试,因此可以先测试离相机较近的三角形T1,然后再测试较远的三角形T2。 如果我们不比较这些三角形的深度,那么在这种情况下我们最终会看到最后测试的三角形 (T2),而实际上我们应该看到 T1。 正如之前多次提到的,这称为可见性(visibility)问题或隐藏面(hidden surface)问题。 对对象进行排序以便正确绘制对象的算法称为可见表面算法或隐藏面去除算法。 我们接下来要研究的深度缓冲区(depth buffer)或z缓冲区(z-buffer)算法就属于此类算法。

可见性问题的一种解决方案是使用深度缓冲区或 z 缓冲区。 深度缓冲区只不过是一个二维浮点数组,其尺寸与帧缓冲区相同,用于在三角形被光栅化时存储对象的深度。 创建此数组时,我们用一个非常大的数字初始化数组中的每个像素。 如果我们发现某个像素与当前三角形重叠,我们会执行以下操作:

  • 我们首先计算像素重叠的三角形上的点的 z 坐标或深度。
  • 然后,我们将当前三角形深度与该像素的深度缓冲区中存储的值进行比较。
  • 如果我们发现深度缓冲区中存储的值大于三角形上点的深度,则新点比深度缓冲区中该像素位置处存储的点更靠近观察者或相机。 然后,深度缓冲区中存储的值将替换为新的深度,并且帧缓冲区将更新为当前的三角形颜色。 另一方面,如果深度缓冲区中存储的值小于当前深度样本,则像素重叠的三角形将被深度缓冲区中当前存储的深度的对象隐藏。

请注意,一旦处理完所有三角形,深度缓冲区就会包含“某种”图像,它表示场景中对象的可见部分与相机之间的“距离”(这不是距离,而是 z- 通过相机可见的每个点的坐标)。 深度缓冲区本质上对于解决可见性问题很有用,但是,它也可以在后处理中用于执行诸如 2D 景深、添加雾等操作。所有这些效果在 3D 中效果更好,但在 2D 通常更快,但结果并不总是像 3D 那样准确。

以下是深度缓冲区算法的伪代码实现:

float *depthBuffer = new float [imageWidth * imageHeight];
// Initialize depth-buffer with a very large number
for (uint32_t y = 0; y < imageHeight; ++y)
    for (uint32_t x = 0; x < imageWidth; ++x)
        depthBuffer[y][x] = INFINITY;

for (each triangle in the scene) {
    // Project triangle vertices
    ...
    // Compute 2D triangle bounding-box
    ...
    for (uint32_t y = bbox.min.y; y <= bbox.max.y; ++y) {
        for (uint32_t x = bbox.min.x; x <= bbox.max.x; ++x) {
           if (pixelOverlapsTriangle(i + 0.5, j + 0.5) {
                // Compute the z-coordinate of the point on the triangle surface
                float z = computeDepth(...);
                // Current point is closest than object stored in depth/frame-buffer
                if (z < depthBuffer[y][x]) {
                     // Update depth-buffer with that depth
                     depthBuffer[y][x] = z;
                     frameBuffer[y][x] = triangleColor;
                }
            }
        } 
    } 
}

2、通过插值求 Z

图 3:我们可以通过使用重心坐标对三角形顶点 z 坐标进行插值来找到 P 的深度吗?

希望深度缓冲区的原理简单易懂。 我们现在需要做的就是解释如何计算深度值。 首先,让我们再次重复一下深度值是什么。 当一个像素与一个三角形重叠时,它会与三角形表面上的一个小表面重叠,正如引言中提到的,我们将其简化为一个点(图1中的点P)。 我们在这里要找到的是该点的 z 坐标。 正如本章前面提到的,如果我们知道三角形顶点的 z 坐标(我们这样做,它们存储在投影点的 z 坐标中),我们需要做的就是使用 P 的重心坐标对这些坐标进行插值(图 4):

图 4:通过线性插值求出点的 y 坐标

计算公式如下:

从技术上讲,这听起来很合理,但不幸的是,它不起作用。 让我们看看为什么。 问题不在于公式本身,它完全没问题。 问题是,一旦三角形的顶点被投影到画布上(一旦我们执行了透视分割),那么 z(我们想要插值的值)就不再在 2D 三角形的表面上线性变化。 通过 2D 示例更容易演示这一点。

秘密就在图 4 中。想象一下,我们想要找到 2D 空间中由两个顶点 V0 和 V1 定义的一条线的“图像”。 画布由水平绿线表示。 这条线距坐标系原点 1 个单位(沿 z 轴)。 如果我们从 V0 和 V1 追踪直线到原点,那么我们就会在两个点处与绿线相交(在图中表示为 V0' 和 V1')。 该点的 z 坐标为 1,因为它们位于画布上,距原点 1 个单位。 使用透视投影可以轻松计算点的 x 坐标。 我们只需要将原始顶点的 x 坐标除以 z 坐标即可。 我们得到:

练习的目标是找到 P 的 z 坐标,P 是由 V0 和 V1 定义的直线上的一个点。 在这个例子中,我们对 P 的了解就是它在绿线上的投影 P' 的位置。 P'的坐标是{0,1}。 该问题类似于尝试查找像素重叠的三角形上的点的 z 坐标。 在我们的示例中,P' 是像素,P 是像素重叠的三角形上的点。 我们现在需要做的是计算 P' 相对于 V0' 和 V1' 的“重心坐标”。 我们将结果值称为 λ 。 与我们的三角形重心坐标一样,λ 也在 [0,1] 范围内。 要找到 λ ,我们只需获取 V0' 和 P' 之间的距离(沿 x 轴),然后将该数字除以 V0' 和 V1' 之间的距离。 如果使用 λ 对原始顶点 V0 和 V1 的 z 坐标进行线性插值来求出 P 的深度,那么我们应该得到数字 4(通过看图我们很容易看出 P 的坐标为 {0 ,4})。 我们首先计算 λ :

如果我们现在对 V0 和 V1 z 坐标进行线性插值来找到 P z 坐标,我们会得到:

这不是我们期望的值! 在本例中使用 P 的“重心坐标”或 λ 对原始顶点 z 坐标进行插值来查找 P z 坐标是行不通的。 为什么? 原因很简单。 透视投影保留线条但不保留距离。 从图 4 中很容易看出,V0 和 P 之间的距离与 V0 和 V1 之间的距离的比率 (0.666) 与 V0' 和 P' 之间的距离与 V0 之间的距离的比率不同。 ' 和 V1' (0.833)。 如果 λ 等于 0.666 就可以正常工作,但问题是,它等于 0.833! 那么,我们如何求出P的z坐标呢?

该问题的解决方案是通过使用 λ 对顶点 V0 和 V1 z 坐标的倒数进行插值来计算 P的z 坐标的倒数。 换句话说,解决方案是:

让我们检查一下它是否有效:

如果现在取这个结果的倒数,我们就得到 P的z 坐标的值 4。这是正确的结果! 如前所述,解决方案是使用重心坐标对顶点的 z 坐标进行线性插值,然后反转结果数以找到 P 的深度(其 z 坐标)。 对于我们的三角形,公式是:

图 5:透视投影保留线条,但不保留距离

现在让我们更正式地研究这个问题。 为什么我们需要对顶点的逆 z 坐标进行插值? 正式的解释有点复杂,如果你愿意的话可以跳过。

让我们考虑相机空间中由两个顶点定义的线,其坐标分别表示为 (X0,Z0) 和 (X1,Z1)。 这些顶点在屏幕上的投影分别表示为 S0 和 S1(在我们的示例中,我们假设相机原点和画布之间的距离为 1,如图 5 所示)。 我们将 S 称为 S0 和 S1 定义的直线上的点。 S 在 2D 线上有一个对应的点 P,其坐标为 (X,Z = 1)(在本例中我们假设投影点的屏幕或垂直线距坐标系原点 1 个单位)。 最后,参数 t 和 q 的定义如下:

我们也可以写成:

因此,可以通过插值计算点 P 的 (X,Z) 坐标(等式 1):

类似地(等式 2):

S 是一个一维点(它已投影在屏幕上),因此它没有 z 坐标。 S 也可以计算为:

因此:

如果我们将分子替换为方程 1,将分母替换为方程 2,则我们得到(方程 3):

我们也得到:

因此(等式 4):

如果现在将方程 3 中的 X0 和 X1 替换为方程 4,我们得到(等式5):

记住等式 1(等式 6):

如果结合方程 5 和 6,我们得到:

可以简化为:

我们现在可以用 q 来表达参数 t:

如果代入等式 6 中的 t,我们得到:

因此可以得到:

这就是我们最终想要得到的公式。

3、其他可见表面算法

正如简介中提到的,z 缓冲区算法属于隐藏表面去除或可见表面算法系列。 这些算法可以分为两类:对象空间算法和图像空间算法。 本课我们没有讨论的“painter”算法属于前者,而z-buffer算法则属于后者。 painter算法背后的概念大致是从后到前绘制或绘制对象。 该技术需要对对象进行深度排序。 正如本章前面所解释的,第一个对象以任意顺序传递到渲染器,然后当两个三角形彼此相交时,很难弄清楚哪个三角形在另一个前面(从而决定应该绘制哪个三角形) 第一的)。 该算法已不再使用,但 z 缓冲区非常常见(GPU 使用它)。


原文链接:可见性和深度缓冲区算法 - BimAnt

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Let‘s Encrypt SSL】使用 acme.sh 给 Nginx 安装 Let’s Encrypt 提供的免费 SSL 证书

安装acme.sh 安装 acme.sh 并设置邮箱用来接受重要通知&#xff0c;如证书快过期未更新通知 curl https://get.acme.sh | sh -s emailmyexample.com执行命令后几秒就安装好了&#xff0c;如果半天没有反应请 CtrlC 后重新执行命令。acme.sh 安装在 ~/.acme.sh 目录下&#xf…

申请Let‘s Encrypt证书

Lets Encrypt官网&#xff1a;https://letsencrypt.org/ Certbot官网&#xff1a;https://certbot.eff.org/ 1、域名控制台添加域名&#xff0c;如test.example.com 2、在服务器添加nginx配置&#xff0c;test.example.com.conf文件内容如下 # test.example.com.conf配置serve…

NCNN 源码学习【三】:数据处理

一、Topic&#xff1a;数据处理 这次我们来一段NCNN应用代码中&#xff0c;除了推理外最重要的一部分代码&#xff0c;数据处理&#xff1a; ncnn::Mat in ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 227, 227);const float mean_v…

无法打开源文件“opencv2/opencv.hpp“

如图报错&#xff0c;看见就非常高血压 解决方案&#xff1a; 1.打开项目属性 第二步&#xff0c;注意你如果跑的是Debug&#xff0c;那么你在项目属性里面设置的必须选择Debug模式&#xff0c;跑的Release模式&#xff0c;则你必须要设置相应的Release模式&#xff01;否则你…

【解决】Windows 11检测提示电脑不支持 TPM 2.0(注意从DTPM改为PTT)

win11升级&#xff0c;tpm不兼容 写在最前面1. 打开电脑健康状况检查2. 开启tpm3. 微星主板AMD平台开启TPM2.0解决电脑健康状况检查显示可以安装win11&#xff0c;但是系统更新里显示无法更新 写在最前面 我想在台式电脑上用win11的专注模式&#xff0c;但win10不支持 1. 打…

湖仓一体架构理论与实践汇总

湖仓一体架构理论与实践汇总 软件研发本质上属于“手工业”。软件研发在很大程度上还是依赖于个人的能力。当软件规模较小时&#xff0c;依赖“手工业”可以解决问题&#xff0c;但是当软件规模大了之后再依赖“手工业”就不行了。 软件的复杂度包含两个层面&#xff1a;软件…

Git篇---第七篇

系列文章目录 文章目录 系列文章目录前言一、如果分支是否已合并为master,你可以通过什么手段知道?二、 什么是SubGit?三、列举工作中常用的几个git命令?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文…

StatusBar、NavigationBar窗口显示在Activity下面之aosp14窗口类bug线索征集

背景&#xff1a; hi&#xff0c;粉丝朋友们&#xff1a; 从上次帮助国际学员解决了一个分屏有黑屏的bug后&#xff0c;相关blog和解决方法 https://blog.csdn.net/learnframework/article/details/134708393 解决方法看b站视频&#xff1a; https://www.bilibili.com/video/B…

常用的建表但范式、反规范化

规范化&#xff1a; 规范化是用于数据库设计的一系列原理和技术&#xff0c;它可以减少表中数据的冗余&#xff0c;增加数据完整性和一致性。通常有很多范式。 第一范式&#xff08;1NF&#xff09;&#xff1a; 常用的三种范式&#xff1a; 表中的字段都是不可再分割的原子属…

Linux---切换目录命令

1. 切换目录命令的使用 命令说明cd 目录切换到指定目录cd ~切换到当前用户的主目录cd ..切换到上一级目录cd .切换到当前目录cd -切换到上一次目录 注意: cd命令切换目录时&#xff0c;这个目录必须存在。cd 后面不写目录等价于cd ~ cd 目录效果图: cd ~效果图: cd ..效果图…

git 实用命令杂记

使用解决冲突的方式合并&#xff0c;将避免简单的自动合并 git merge origin/dev --strategyresolve清理本地已经合并到 dev 的分支 git branch --merged | grep -v dev | xargs -n 1 git branch -d分支清理 Git 之删除本地无用分支_dearfulan 的博客 - CSDN 博客_git 删除本…

同义词替换论文降重工具 快码论文

大家好&#xff0c;今天来聊聊同义词替换论文降重工具&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 标题&#xff1a;同义词替换论文降重工具&#xff1a;原理、应用与优势 一、引言 在学术研究中&am…

数据入表 | 详解数据资产会计核算与企业应对

从2015年《促进大数据发展行动纲要》到2022年《数据20条》到2023年8月份出台了《企业数据资源相关会计处理暂行规定》&#xff0c;可见国家层面对数据的重视和探索如何进一步挖掘数据价值&#xff0c;发挥数据的应用潜力。一石激起千层浪&#xff0c;面对如此重要的规定&#x…

高通平台开发系列讲解(外设篇)高通平台EMMC适配说明

文章目录 一、EMMC的内部框图说明二、EMMC 设备树配置三、EMMC 内核配置四、EMMC 源码沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要图解高通平台 EMMC适配说明。 eMMC(嵌入式多媒体卡)是一种集成了闪存存储器和控制器的存储芯片,通常用于嵌入式设备中,…

WPF-一个简单登录界面

一个简单登录界面 文章目录 一个简单登录界面一、效果展示二、准备代码 一、效果展示 二、准备代码 创建一个WPF工程&#xff0c;创建名为 Login5 的WPF项目。 添加Nuget包 MaterialDesignThemes 界面的整体布局和样式代码 <Window x:Class"Login5.MainWindow&quo…

01详解Gateway服务网关的功能,实现,分类.工作流程

Gateway服务网关 网关功能 Gateway网关是是所有微服务的统一入口, 网关的核心功能特性主要体现在请求路由,权限控制,限流三部分 路由: 由于网关不能处理业务,所以网关需要根据某种规则(断言)把请求转发给匹配的主机或者接口上,这个转发的过程就叫做路由负载均衡: 当路由的目…

听觉动态范围是什么?

什么是动态范围&#xff0c;它如何影响您的听力&#xff1f; 简单地说&#xff0c;动态范围就是您可用的听力范围。它是从你能听到的最柔和的声音&#xff08;你的听力阈值&#xff09;到声音非常响亮的地方&#xff08;不舒服的响度水平&#xff09;的分贝&#xff08;dB&…

Web前端 ---- 【Vue】Vue路由传参(query和params)

目录 前言 为什么用路由 路由route和路由器router Vue中路由的工作原理 安装配置vue-router 使用VueRouter 多级路由 路由传参 query传参 params传参 前言 本文介绍路由相关知识路由传参 为什么用路由 为了单页面应用开发&#xff0c;只更换组件&#xff0c;不频繁刷…

Postman接口测试工具使用

一、前言 在前后端分离开发时&#xff0c;后端工作人员完成系统接口开发后&#xff0c;需要与前端人员对接&#xff0c;测试调试接口&#xff0c;验证接口的正确性可用性。而这要求前端开发进度和后端进度保持基本一致&#xff0c;任何一方的进度跟不上&#xff0c;都无法及…

跨境电商如何利用跨境客服软件提升销售额

随着全球化的推进&#xff0c;跨境电商成为了许多企业拓展市场的重要途径。然而&#xff0c;跨境电商面临着语言、文化、时差等多种挑战&#xff0c;为了提供更好的客户服务并提升销售额&#xff0c;跨境电商需要利用跨境客服软件。本文将探讨跨境电商如何利用跨境客服软件来提…