【GAMES202】Real-Time Environment Mapping1—实时环境光照1

news2024/12/23 6:15:57

一、Distance field soft shadows

Inigo Quilez :: computer graphics, mathematics, shaders, fractals, demoscene and more (iquilezles.org)

在开始我们的实时环境光照之前,我们再说一种现在的实现实时软阴影的方式,也就是Distance field soft shadows,也就是距离场软阴影,它的效果图如上图。

1.距离场回顾

c - Signed distance field calculation bug - Stack Overflow

关于距离场/距离函数我们在GAMES101的几何章节中提到过,它定义了空间中每一个点到某一物体表面的最小距离。它可以有正负号,代表该点在物体的内部还是外部,这样一来它就不仅定义了距离,而是定义了有向距离,所以我们一般简称为SDF。上图所示为字母A的SDF的可视化图。

在GAMES101里我们提到过这个例子,假设上图中的黑色是某一物体,A时刻在一个位置,而在B时刻移动到了另外一个位置,我们想得到它的边界是如何运动的,如果只用lerp线性插值只能得到全灰色的一条过渡带,如上图。而如果我们先把A和B时刻物体的SDF求出来(图中蓝色表示在物体内,所以SDF<0),然后再对A和B的SDF进行lerp线性插值就可以恢复出来物体的边界和运动过程。

SDF还有一个比较好的用处,就是可以得到任意两个几何的blending,而不需要关心这两个几何之间的拓扑关系。如上图球形靠近正方形融合的过程就是使用了SDF进行blend,由此可见SDF可以得到一个非常好的过度。SDF背后的理论为Optimal Transport,也就是最优传输,有兴趣的朋友可以自行学习,这里就不提了。

2.光线步进—Ray marching

Mesh Distance Fields in Unreal Engine | Unreal Engine 5.2 Documentation

SDF另一个很好的用处是Ray Marching也就是光线步进,这里在GAMES101里并没有提到,它实际上也就是一种光线追踪。由于我们定义了SDF,那么我们就知道场景空间中每个点到场景每个物体表面的最短距离,我们就可以用一个球表示当前光线步进的安全距离(该距离为各个SDF中最小的距离),因为SDF告诉我们在这个球的范围中,我们步进半径的距离是不会穿过任何物体的,如上图所示。最后,不断步进直到与物体表面距离足够小或者追踪足够远了仍然没打到任何物体停下来,这就是Ray Marching。

3.SDF生成软阴影

Mesh Distance Fields in Unreal Engine | Unreal Engine 5.2 Documentation

接下来我们分析如何利用SDF生成软阴影,也就是Distance field soft shadows,在上面说Ray Marching的时候,我们提到了SDF为我们的光线步进提供了一个安全距离的参考,使得我们不会步进步长过大。而SDF在生成软阴影中则为我们提供了一个安全角度,如上图所示。从上图我们可以观察到,当我们从一个着色点出发向一个光源,其中在这条线上通过SDF可以为我们提供一个安全角,取决于这条线上最短的SDF,而显然安全角越小,这个点被挡住的光就越多,自然也就越黑。

那么这个安全角如何计算呢?光线步进的距离我们知道了,这也就是三角形的斜边,然后SDF的距离那也就是直角边,这样只需要计算arc sinθ就可以了,但实际上人们很少做这种复杂的计算。

Inigo Quilez :: computer graphics, mathematics, shaders, fractals, demoscene and more (iquilezles.org)

 如上图,正常的计算公式是左边的arcsin\frac{SDF(p)}{\left | p-o \right |},而事实上人们用的是右面的公式min\left \{ \frac{k\cdot SDF(p)}{\left | p-o \right |} ,1.0\right \},因为这个公式就已经能判断阴影的大小了,反三角的计算又很复杂,所以没必要算的那么精确,直接乘以一个常数k代替arcsin即可,最后为了保证visibility不能超过1,再做一个min截断即可。至于k在一定程度上决定了半影区过度的速度或者说软硬程度,如果k特别大,得出的阴影自然和硬阴影一样。(例如k取100,则过渡带为0~0.01,非常小)

SDF可视化:Mesh Distance Fields in Unreal Engine | Unreal Engine 5.2 Documentation

4.Distance field soft shadows的问题

(1)优点

1.快*,相比Shadow map不考虑预计算花费的时间,查找SDF比Shadow map的遍历纹理快。

2.高质量

(2)缺点

1.场景的SDF需要预计算

2.存储SDF需要大量的空间

3.如果有物体发生形变,需要重新计算SDF

5.SDF的存储

我们提到SDF是一个三维空间的数据,相当于我们要存储一个三维的纹理,这么大的数据量我们怎么存储呢?事实上,人们经常用我们之前GAMES101提到的空间数据结构来存储SDF,包括KD树,八叉树等等来划分空间,然后划分的某些区域可能离场景中绝大多数物体很远,那我们自然就没必要去把它划分的那么精细,可以直接用一个大的节点来记录。划分直到在一个节点内,它的SDF变化非常剧烈的情况下,就精细的划分记录在叶子节点上。

二、环境光照的着色—Shading from environment lighting

1.Image-Based Lighting (IBL)

在GAMES101里我们提到过环境光照的解决方法,就是把环境光记录在一张图上,是球也好或者是Cubemap也好(默认环境光来自无限远),都属于基于图像的光照,也就是IBL。那么给定了我们环境光照,我们如何对任意一个着色点(不考虑阴影遮挡)计算它的环境光着色呢?

计算环境光着色,我们自然想到去解渲染方程。实时渲染中我们经常把Visibility项拆出来,而又因为我们现在不考虑Visibility,所以现在只剩下了来自四面八方的环境光照Li,又因为要考虑法线方向的正半球积分,所以要么积分域为Ω+。

那么如何解这个积分呢,在GAMES101中我们提到可以用蒙特卡洛积分法来解渲染方程,但显然这是非常非常慢的,因为涉及到大量的采样。

那么我们有没有不采样的方法呢?

2.Prefiltering

这里有一个很有趣的观察,我们知道前面我们的渲染方程形式是对Li和BRDF的乘积做了一个积分,在前面我们提到过一个约等式可以把乘积的积分改为积分的乘积,但是有两个条件,一个是要么其中一个函数的积分线较小,要么比较smooth。而通过观察我们发现,Glossy的BRDF正好是积分域比较小,而Diffuse的BRDF正好足够smooth,这样就正好满足我们约等式准确的条件。

如上图,我们通过约等式就可以把Light项拆出来,(在当时计算阴影的时候我们拆的是Visibility项)。所以这个约等式是可以根据不同情况把不同项当成g(x)的。

那么拆出来这一项,也就是黄色方框里的项是在干嘛呢?可以看到对区域做积分之后又除了一个积分,显然是归一化,这就相当于给平均了,而又是一定范围内的平均,那显然就是“模糊”操作。这里我们把它叫做Prefiltering,我可以滤波环境光,至于滤波核取多大,自然就取决于BRDF占多大。Pre体现在哪里呢?这里是在进行渲染之前就已经把这些图做好模糊了,已经预生成好了,所以叫Prefiltering。至于不同大小滤波核模糊的结果的查询,可以参考Mipmap,用层与层之间的三线性插值即可。

如上图左图即为通过分布采样点加权平均来做Shading,而实际上它和上图右—先对Lighting做好滤波,然后再根据镜面反射方向直接查询,这两种操作本质是几乎相同的。


3.Split Sum 

解决了前半部分,我们来解决后半部分,也就是上图红框的部分,这里其实我们也可以通过预计算解决,但是很麻烦,就算我们先设定好了BRDF(假如是微表面),我们还是要先把BRDF的这些参数所有的可能都考虑进去,包括NDF,入射出射角,数据太庞大了,所以单纯的预计算显然行不通。

我们先回顾一下GAMES101里提到的微表面模型的近似,其中菲涅尔项的近似有两个参数,一个是基础反射率R0,一个是入射出射角θ,而法线分布函数也有一个近似高斯的分布,里面同样有两个参数,分别是roughness粗糙度αθ。那现在看来这仍然是一个三维的数据表格,预计算仍然很困难。

但是人们法发现,渲染方程里的BRDF项中的菲涅尔项可以单独拆出来(如上图的先除以F再乘上展开的菲涅尔项),然后由此就可以把R0拆出来从而不受积分影响。

这样一来,R0不受积分影响,我们只需要预计算两个参数了,那自然也就是一张纹理就可以搞定了,u存θ,v存Roughness,纹理上任意一点存储积分值即可,而方程的两个部分甚至可以用一张纹理的两个通道存储。

 可以看到,通过合理的近似,预计算环境光照和蒙特卡洛积分得出的结果几乎无差别。

这种通过把乘积积分拆成积分乘积的方法叫做Split sum方法,因为在工业界大家往往把积分写成求和。

参考:

GAMES202_Lecture_05 (ucsb.edu)

Lecture5 Real-time Environment Mapping_哔哩哔哩_bilibili

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

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

相关文章

SpringBoot实现文件上传和下载笔记分享(提供Gitee源码)

前言&#xff1a;这边汇总了一下目前SpringBoot项目当中常见文件上传和下载的功能&#xff0c;一共三种常见的下载方式和一种上传方式&#xff0c;特此做一个笔记分享。 目录 一、pom依赖 二、yml配置文件 三、文件下载 3.1、使用Spring框架提供的下载方式 3.2、通过IOUti…

分布式 - 服务器Nginx:一小时入门系列之 return 指令

文章目录 1. return 指令语法2. return code URL 示例3. return code text 示例4. return URL 示例 1. return 指令语法 return指令用于立即停止当前请求的处理&#xff0c;并返回指定的HTTP状态码和响应头信息&#xff0c;它可以用于在Nginx中生成自定义错误页面&#xff0c;…

分布式事务-seata框架

文章目录 分布式事务0.学习目标1.分布式事务问题1.1.本地事务1.2.分布式事务1.3.演示分布式事务问题 2.理论基础2.1.CAP定理2.1.1.一致性2.1.2.可用性2.1.3.分区容错2.1.4.矛盾 2.2.BASE理论2.3.解决分布式事务的思路 3.初识Seata3.1.Seata的架构3.2.部署TC服务3.3.微服务集成S…

CAPL - Panel和TestModule结合实现测试项可选

目录 一、定义脚本编号和脚本组编号 1、测试组定义 2、测试脚本编号定义

【C++】初步认识模板

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、泛型编程二、函数模板2.1 函…

Java10(异常处理)

0.复习面向对象 1.异常的体系结构 异常&#xff1a;在Java语言中&#xff0c;将程序执行中发生的不正常情况.(开发中的语法错误和逻辑错误不是异常) 异常事件分两类&#xff08;它们上一级为java.lang.Throwable&#xff09;&#xff1a; Error Java虚拟机无法解决的严重问…

算法通过村第三关-数组黄金笔记|数组难解

文章目录 前言数组中出现超过一半的数字数组中只出现一次的数字颜色的分类问题(荷兰国旗问题)基于冒泡排序的双指针&#xff08;快慢指针&#xff09;基于快排的双指针&#xff08;对撞指针&#xff09; 总结 前言 提示&#xff1a;苦不来自外在环境中的人、事、物&#xff0c;…

最新人工智能源码搭建部署教程/ChatGPT程序源码/AI系统/H5端+微信公众号版本源码

一、AI系统 如何搭建部署人工智能源码、AI创作系统、ChatGPT系统呢&#xff1f;小编这里写一个详细图文教程吧&#xff01; SparkAi使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到AIGC系统&#xff01; 1.1 程序核心功能 程序已支持ChatGPT3.5/GPT-4提问、AI绘画、…

Vue3.0极速入门- 目录和文件说明

目录结构 以下文件均为npm create helloworld自动生成的文件目录结构 目录截图 目录说明 目录/文件说明node_modulesnpm 加载的项目依赖模块src这里是我们要开发的目录&#xff0c;基本上要做的事情都在这个目录里assets放置一些图片&#xff0c;如logo等。componentsvue组件…

加快edge网页的下载速度

1、 edge://flags/#enable-parallel-downloading、 2、 点击enabled

Leetcode每日一题:1448. 统计二叉树中好节点的数目(2023.8.25 C++)

目录 1448. 统计二叉树中好节点的数目 题目描述&#xff1a; 实现代码与解析&#xff1a; dfs 原理思路&#xff1a; 1448. 统计二叉树中好节点的数目 题目描述&#xff1a; 给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。 「好节点」X 定义为&…

谈谈子网划分的定义、作用、划分方式以及案例

个人主页&#xff1a;insist--个人主页​​​​​​ 本文专栏&#xff1a;网络基础——带你走进网络世界 本专栏会持续更新网络基础知识&#xff0c;希望大家多多支持&#xff0c;让我们一起探索这个神奇而广阔的网络世界。 目录 一、子网划分的定义 二、子网掩码的作用 1、…

[管理与领导-51]:IT基层管理者 - 8项核心技能 - 6 - 流程

前言&#xff1a; 管理者存在的价值就是制定目标&#xff0c;即目标管理、通过团队&#xff08;他人&#xff09;拿到结果。 要想通过他人拿到结果&#xff1a; &#xff08;1&#xff09;目标&#xff1a;制定符合SMART原则的符合业务需求的目标&#xff0c;团队跳一跳就可以…

【mysql是怎样运行的】-EXPLAIN详解

文章目录 1.基本语法2. EXPLAIN各列作用1. table2. id3. select_type4. partitions5. type 1.基本语法 EXPLAIN SELECT select_options #或者 DESCRIBE SELECT select_optionsEXPLAIN 语句输出的各个列的作用如下&#xff1a; 列名描述id在一个大的查询语句中每个SELECT关键…

Unity——后期处理举例

Post Processing&#xff08;后期处理&#xff09;并不属于特效&#xff0c;但现代的特效表现离不开后期处理的支持。本文以眩光&#xff08;Bloom&#xff09;为例&#xff0c;展示一种明亮的激光的制作方法 1、安装后期处理扩展包 较新的Unity版本已经内置了新版的后期处理扩…

2022年03月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;拦截导弹 某国为了防御敌国的导弹袭击&#xff0c; 发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷&#xff1a; 虽然它的第一发炮弹能够到达任意的高度&#xff0c;但是以后每一发炮弹都不能高于前一发的高度。 某天&#xff0c; 雷达捕捉到敌国的…

Linux内核学习(十)—— 块 I/O 层(基于Linux 2.6内核)

目录 一、剖析一个块设备 二、缓冲区和缓冲区头 三、bio 结构体 四、请求队列 五、I/O 调度程序 系统中能够随机&#xff08;不需要按顺序&#xff09;访问固定大小数据片&#xff08;chunks&#xff09;的硬件设备称作块设备&#xff0c;这些固定大小的数据片就称作块。最…

项目进度管理(4-1)关键链法

1 关键链法产生的背景 关键链法&#xff08;Critical Chain Method&#xff0c;CCM&#xff09;起源于20世纪80年代&#xff0c;是由Eliyahu M. Goldratt在他的著作《关键链》&#xff08;"Critical Chain"&#xff09;中首次提出和阐述的。Eliyahu M. Goldratt是以…

Java 时间String转Date类型

Testpublic void testTime() throws ParseException {SimpleDateFormat format new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//法一&#xff1a;String time111 "2023-08-14 18:13:10";Date date format.parse(time111);System.out.println("法一…

4.18 TCP 和 UDP 可以使用同一个端口吗?

目录 TCP 和 UDP 可以同时绑定相同的端口吗&#xff1f; 多个 TCP 服务进程可以绑定同一个端口吗&#xff1f; 重启 TCP 服务进程时&#xff0c;为什么会有“Address in use”的报错信息&#xff1f; 重启 TCP 服务进程时&#xff0c;如何避免“Address in use”的报错信息…