从零开始的OpenGL光栅化渲染器构建3-法线贴图和视差贴图

前言

我们可以用一张纹理贴图来表现物体表面的基础反射颜色,也可以用一张镜面反射贴图,来指派表面是否产生高光。除此之外,我们可以用贴图来存储表面的法线信息,以及高度信息,从而让渲染效果更加精细。

法线贴图

我们可以让每一个fragment采用自己的不同的法线,这样就可以获得一种表面看起来复杂得多的幻觉。

采用法线贴图有一个问题,如果物体位姿发生变化了,通过采样获得的法线如何变换?可以记录下物体的初始位姿,如果物体发生了旋转,再对采样获得的法线进行旋转。

也可以变换到切线空间。在一个不同的坐标空间中进行光照,这个坐标空间里,法线贴图向量总是指向这个坐标空间的正z方向;所有的光照向量都相对于这个正z方向进行变换。这样我们就能始终使用同样的法线贴图,不管朝向问题。这个坐标空间叫做切线空间。
在这里插入图片描述

对于一个三角形来说,它的切线空间可以这样计算, N N N轴可以由三角形的两条边 E 1 , E 2 E_1,E_2 E1,E2叉乘得出, T T T轴和 B B B轴和 U V UV UV的方向对齐,要求解 T T T B B B轴,我们可以将三角形的边 E 1 E_1 E1 E 2 E_2 E2写成 T T T轴和 B B B轴的线性组合:
E 1 = Δ U 1 T + Δ V 1 B E 2 = Δ U 2 T + Δ V 2 B E_1 = \Delta U_1T + \Delta V_1B\\ E_2 = \Delta U_2T + \Delta V_2B E1=ΔU1T+ΔV1BE2=ΔU2T+ΔV2B
其中 E 1 E_1 E1 E 2 E_2 E2是这个三角形在本地坐标系中的坐标,可以参考这篇文章。

对上述公式进行进一步计算,我们可以求得切线空间的三个轴,以此作为切线空间的表示。

因为模型在uv上是平铺开的,因此能够利用uv坐标来计算切线和副切线,在其他地方也不好计算。

可以说,一个三角形(或者一个最小绘制单元,如四边形)中的所有点,位于同一个切线空间中。

现在我们有了TBN矩阵,如果来使用它呢?通常来说有两种方式使用它,我们会把这两种方式都说明一下:

  1. 我们直接使用TBN矩阵,这个矩阵可以把切线坐标空间的向量转换到世界坐标空间。因此我们把它传给片段着色器中,把通过采样得到的法线坐标左乘上TBN矩阵,转换到世界坐标空间中,这样所有法线和其他光照变量就在同一个坐标系中了。
  2. 我们也可以使用TBN矩阵的逆矩阵,这个矩阵可以把世界坐标空间的向量转换到切线坐标空间。因此我们使用这个矩阵左乘其他光照变量,把他们转换到切线空间,这样法线和其他光照变量再一次在一个坐标系中了。

第二种方法看似要做的更多,它还需要在像素着色器中进行更多的乘法操作,所以为何还用第二种方法呢?

将向量从世界空间转换到切线空间有个额外好处,**我们可以把所有相关向量在顶点着色器中转换到切线空间,不用在像素着色器中做这件事。**这是可行的,因为lightPos和viewPos不是每个fragment运行都要改变,对于fs_in.FragPos,我们也可以在顶点着色器计算它的切线空间位置。基本上,不需要把任何向量在像素着色器中进行变换,而第一种方法中就是必须的,因为采样出来的法线向量对于每个像素着色器都不一样。这是一个极佳的优化,因为顶点着色器通常比像素着色器运行的少。

一张法线贴图的渲染效果图如下:

在这里插入图片描述

视差贴图

视差贴图属于位移贴图(Displacement Mapping)技术的一种,它对根据储存在纹理中的几何信息对顶点进行位移或偏移。一种实现的方式是比如有1000个顶点,根据纹理中的数据对平面特定区域的顶点的高度进行位移。

视差贴图背后的思想是修改纹理坐标使一个fragment的表面看起来比实际的更高或者更低,所有这些都根据观察方向和高度贴图。为了理解它如何工作,看看下面砖块表面的图片:

在这里插入图片描述

这里粗糙的红线代表高度贴图中的数值的立体表达,向量V¯代表观察方向。如果平面进行实际位移,观察者会在点B看到表面。然而我们的平面没有实际上进行位移,观察方向将在点A与平面接触。视差贴图的目的是,在A位置上的fragment不再使用点A的纹理坐标而是使用点B的。随后我们用点B的纹理坐标采样,观察者就像看到了点B一样。

这个技巧就是描述如何从点A得到点B的纹理坐标。视差贴图尝试通过对从fragment到观察者的方向向量V¯进行缩放的方式解决这个问题,缩放的大小是A处fragment的高度。所以我们将V¯的长度缩放为高度贴图在点A处H(A)采样得来的值。下图展示了经缩放得到的向量P¯:

在这里插入图片描述

我们随后选出P¯以及这个向量与平面对齐的坐标作为纹理坐标的偏移量。这样能行是因为向量P¯是使用从高度贴图得到的高度值计算出来的,所以一个fragment的高度越高位移的量越大。

视差贴图和法线贴图同理,应用于切线空间。

实际在采样的过程中,是反着来的,视差贴图中每个位置的值代表着深度,这是因为使用反色高度贴图(也叫深度贴图)去模拟深度比模拟高度更容易。

在这里插入图片描述

如上图所示,相机看向A这个位置,此时从视差贴图中采样,获得深度值H(A),并记录下此时视线方向在xy上的分量 V ′ V' V。将深度值H(A)乘上一个系数,再乘以 V ′ V' V,我们可以获得一个偏移量 Δ \Delta Δ,A加上这个偏移量 Δ \Delta Δ,得到位置C。我们将位置C的颜色返回。对应到实现上, 这一过程的核心代码如下:

vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{ float height =  texture(depthMap, texCoords).r;    vec2 p = viewDir.xy / viewDir.z * (height * height_scale);return texCoords - p;    
}

下图便是视差贴图的渲染效果图:

在这里插入图片描述

陡峭视差贴图

陡峭视差映射(Steep Parallax Mapping)是视差映射的扩展,原则是一样的,但不是使用一个样本而是多个样本来确定向量P¯到B。即使在陡峭的高度变化的情况下,它也能得到更好的结果,原因在于该技术通过增加采样的数量提高了精确性。

陡峭视差映射的基本思想是将总深度范围划分为同一个深度/高度的多个层。从每个层中我们沿着P¯方向移动采样纹理坐标,直到我们找到一个采样低于当前层的深度值。看看下面的图片:

在这里插入图片描述

我们从上到下遍历深度层,我们把每个深度层和储存在深度贴图中的它的深度值进行对比。如果这个层的深度值小于深度贴图的值,就意味着这一层的P¯向量部分在表面之下。我们继续这个处理过程直到有一层的深度高于储存在深度贴图中的值:这个点就在(经过位移的)表面下方。

这个例子中我们可以看到第二层(D(2) = 0.73)的深度贴图的值仍低于第二层的深度值0.4,所以我们继续。下一次迭代,这一层的深度值0.6大于深度贴图中采样的深度值(D(3) = 0.37)。我们便可以假设第三层向量P¯是可用的位移几何位置。我们可以采用 T 3 T_3 T3这个位置作为偏移后的纹理坐标。

此时的结果如下图所示:

在这里插入图片描述

视差遮蔽映射

视差遮蔽映射(Parallax Occlusion Mapping)和陡峭视差映射的原则相同,但不是用触碰的第一个深度层的纹理坐标,而是在触碰之前和之后,在深度层之间进行线性插值。我们根据表面的高度距离啷个深度层的深度层值的距离来确定线性插值的大小。看看下面的图片就能了解它是如何工作的:

在这里插入图片描述

相比于陡峭视差映射,这一步加上了一个插值。

最终渲染出的效果图如下所示:

在这里插入图片描述

参考

https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/

https://learnopengl-cn.github.io/05%20Advanced%20Lighting/05%20Parallax%20Mapping/

https://zhuanlan.zhihu.com/p/446957364

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

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

相关文章

通过浏览器URL地址,5分钟内渗透你的网站!很刑很可拷!

今天我来带大家简单渗透一个小破站,通过这个案例,让你深入了解为什么很多公司都需要紧急修复各个中间件的漏洞以及进行URL解析拦截等重要操作。这些措施的目的是为了保护网站和系统的安全性。如果不及时升级和修复漏洞,你就等着被黑客攻击吧&…

浮点数详解

目录 1.概述 2.浮点数的编码方式 2.1.float类型的IEEE编码 2.2.double类型的IEEE编码 2.3.现场问题 2.4.总结 1.概述 计算机也需要运算和存储数学中的实数。在计算机的发展过程中,曾产生过多种存储实数的方式,有的现在已经很少使用了。不管如何存储…

LeetCode 48 旋转图像

题目描述 旋转图像 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1: 输入:matrix [[1,2,3],[4…

Vue.js 3 项目开发:迈向现代化前端开发的必经之路

文章目录 一、Vue.js 3简介二、Vue.js 3新特性1. Composition API2. 更好的性能3. 更好的TypeScript支持4. 更多的生命周期钩子5. 更好的自定义指令API 三、Vue.js 3项目开发实践1. 搭建开发环境2. 项目结构规划3. 组件开发4. 路由管理5. 状态管理6. 测试与部署 《Vue.js 3企业…

工业计算机应用——物流行业

工业计算机在物流行业的应用 随着全球化和电商的快速发展,物流行业已经成为现代经济体系中的重要支柱。在这个高度自动化的行业中,工业计算机扮演着至关重要的角色。本文将深入探讨工业计算机在物流行业的应用及其优势。 一、工业计算机在物流行业的应用场景 仓储管理工业计…

Viessmann Vitogate RCE漏洞复现(CVE-2023-45852)

0x01 产品简介 Viessmann Vitogate 300是用于将Viessmann LON连接到BACnet或Modbus的网关。 0x02 漏洞概述 Vitogate 300 组件/cgi-bin/vitogate.cgi中的一个问题允许未经身份验证的攻击者绕过身份验证,通过特制的请求执行任意命令,可导致服务器失陷。…

Linux如何将文件或目录打成rpm包? -- fpm打包详解

👨‍🎓博主简介 🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支…

【江科大】STM32:串口HEX/文本数据接收和发送(代码部分)(下)

串口发送 #include "stm32f10x.h" // Device header#include<stdio.h> #include<stdarg.h> void Serial_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPI…

latex画边框框加粗的表格

示例代码 \begin{table}[H]% 这四个距离分别控制toprule和buttomrule的上、下空白间距&#xff0c;如果不是0的话以后竖线和横线连不起来\abovetopsep0pt\aboverulesep0pt\belowrulesep0pt\belowbottomsep0pt\centering% \heavyrulewidth是\toprule和\bottomrule的默认宽度&am…

如何做好培训管理?推荐使用这款培训管理系统(内附详细步骤+免费模板)

本文将为大家讲解&#xff1a;1、如何做好培训管理&#xff1f;2、如何使用零代码平台搭建培训管理系统&#xff1f; 培训管理&#xff0c;作为企业人力资源管理的核心环节&#xff0c;对于确保员工具备完成任务所需的专业知识和技能发挥着至关重要的作用。它不仅是提升员工绩…

北斗卫星为野外科考人员提供安全保障

北斗卫星为野外科考人员提供安全保障 自第二次青藏高原综合科学考察研究启动以来&#xff0c;青海不断提升科考服务保障能力&#xff0c;推动科考全程信息化&#xff0c;有效促进科考成果转化。 为保障科考人员的人身安全&#xff0c;青海省青藏科学考察服务中心开发了基于北…

第08章_面向对象编程(高级)(static,单例设计模式,理解mian方法,代码块,final,抽象类与抽象方法,接口,内部类,枚举类,注解,包装类)

文章目录 第08章_面向对象编程(高级)本章专题与脉络1. 关键字&#xff1a;static1.1 类属性、类方法的设计思想1.2 static关键字1.3 静态变量1.3.1 语法格式1.3.2 静态变量的特点1.3.3 举例1.3.4 内存解析 1.4 静态方法1.4.1 语法格式1.4.2 静态方法的特点1.4.3 举例 1.5 练习 …

UI设计中的插画运用优势(下)

6. 插画赋予设计以美学价值&#xff0c;更容易被接受 即使所有人都在分析和争论产品的可用性和易用性&#xff0c;大家在对美的追求上&#xff0c;始终保持着一致的态度。一个设计是否具备可取性&#xff0c;是否能够通过甲方、客户和实际用户&#xff0c;是每个设计人都需要面…

高频一体式读写器的应用及其原理

高频一体式读写器作为一款读写设备&#xff0c;将RFID读写模块和天线集于一体&#xff0c;通过天线与RFID标签进行无线通信&#xff0c;实现对标签的识别和内存数据的读出或写入操作。具备安全、准确、快速、扩展、兼容性强等特点&#xff0c;具备非接触识别、远距离识别、环境…

Laravel 10.x 里如何使用ffmpeg

原理上很简单&#xff0c;就是使用命令行去调用ffmpeg&#xff0c;然后分析一下输出是不是有错误。 安装 首先安装 symfony/process&#xff0c;主要用于包装一下&#xff0c;用来代替 exec, passthru, shell_exec and system 。 composer require symfony/process composer…

PowerShell install 一键部署grafana

grafana 前言 Grafana 是一款开源的数据可视化和监控仪表盘工具。它提供了丰富的数据查询、可视化和报警功能,可用于实时监控、数据分析和故障排除等领域。 通过 Grafana,您可以连接到各种不同的数据源,包括时序数据库(如 Prometheus、InfluxDB)和关系型数据库(如 MySQ…

linux性能优化-磁盘I_O优化

1.文件系统 1.1.文件系统的工作原理 文件系统是在磁盘的基础上&#xff0c;提供了一个用来管理文件的树状结构。 接下来我们就看看Linux 文件系统的工作原理。 1.1.1索引节点和目录项 在 Linux 中一切皆文件 ,文件系统,本身是对存储设备上的文件&#xff0c;进行组织管理的…

【Linux】—— 共享内存

本期我将要带大家学习的是有关进程间通信的另一种方式——共享内存。共享内存是一种用于进程间通信的高效机制&#xff0c;允许多个进程访问和操作同一块内存区域。 目录 &#xff08;一&#xff09;深刻理解共享内存 1.1 概念解释 1.2 共享内存原理 1.3 共享内存数据结构 …

基于SpringBoot的药品管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

数据治理能解决AI疲劳问题吗?

这篇文章强调了AI疲劳开始的两个阶段&#xff0c;并介绍了数据质量报告等数据治理措施如何能够推动构建值得信赖和健壮的模型。 数据治理和AI疲劳听起来像是两个不同的概念&#xff0c;但两者之间有着内在的联系。为了更好地理解它&#xff0c;让我们从它们的定义开始。 数据治…