【GAMES202】Real-Time Global Illumination(in 3D)—实时全局光照(3D空间)

一、SH for Glossy transport

1.Diffuse PRT回顾

上篇我们介绍了PRT,并以Diffuse的BRDF作为例子分析了预计算的部分,包括Lighting和Light transport,如上图所示。 包括我们还提到了SH,可以用SH的有限阶近似拟合球面函数,然后计算。

这里和上篇的推导方式不太一样,我们上篇是把Lighting项用SH分解然后交换积分和求和符号,最后变成了两个向量的点乘。而这次我们把Lighting项用SH分解,把Light transport项也用SH分解,最后得到了两个求和,以及右边的两个基函数的乘积再积分(product integral) ,感觉不太一样?从O(n)变成了O(n2)?

这里就可以用到SH的性质,正交性,显然两个基函数的product integral的操作正如同把一个基函数投影到另一个基函数上一样,类比于三维空间中把x轴投影到y轴,结果是0,除非是把x轴投影到x轴,那么结果是1,由此我们知道,只有Bp和Bq是相同基函数的情况下,右边这个东西才不等于0而是等于1。

由此也就相当于两个一维向量组成的矩阵,但只有矩阵对角线上有值,那么仍然是O(n),我们的上篇的推导仍然成立。

2.Glossy PRT

接下来看Glossy的情况,我们之前说Diffuse的PRT好做是因为,Diffuse的BRDF是一个常数,而Glossy的BRDF显然不是一个常数,它是一个完整的四维的函数。 

这里我们仍然把Lighting投影到SH上,然后把Light transport也投影到SH上,但是最后得到的结果就不是简单的两个向量的点乘了,Ti变成了T(o),原因正是因为BRDF此时不再是常数了,此时任给一个方向o,我们都可以得到一个BRDF和相应的T(o),也就是说,不同的o得到的向量不同,也就是说我们最后得到的不再是一个向量Ti,而是一个函数T(o)。

换个角度理解,我们知道Diffuse的反射是和视角无关的,而Glossy则不同,Glossy和视角方向是有关的,这也符合最后得到的结果L(o)是一个关于观察方向o的函数。 

那我们怎么处理呢?原本的四维被我们投影到二维的SH上变成了二维的Light transport,那我们可不可以再做一次投影呢?答案是可以,只不过这时候transport就不再是一个向量了,而是一个矩阵,如上图所示。最后的结果自然是一个关于观察方向o的函数L(o)了,也就是一个向量。

当然Glossy的PRT也有代价,首先是预计算的存储,如果想用前五阶的SH拟合,那就是25*25=625个基函数的计算结果。其次因为不再是向量与向量相乘,而是向量与矩阵相乘,计算的开销也会增加。正常情况下人们通常会用3,4,5阶的SH,相应的高频BRDF使用的阶数就高一些。

对于特别高频的情况(接近镜面反射),一般会采用其它的基函数来投影,因为SH表达高频的效果很差,或者另一种解决思路就是直接采样就可以了,因为镜面反射已经知道了是如何反射的。

3.PRT for Interreflections and Caustics

PRT同样可以做Interreflection,也就是自身反射自身的效果。

我们先总结一下传播路径,如上图所示,其中LE表示光源(Light)直接到达眼睛(Eye),LGE自然表示光源(Light)打到Glossy的物体,再进入眼睛(Eye),一个通用的表达L(D|G)*E,因为物体要么是Diffuse的要么是Glossy(Specular可以当作特殊的Glossy)的,*代表可以反射多次,然后进入人的眼睛。

如上图的茶壶所示,在多了一次光线的bounce之后,壶身可以反射到自身的壶嘴,那自然bounce越多反射的也就越多,也就越接近真实的光线传播。

另一种常见的传播路径,Caustics(焦散),通常用LS*(D|G)*E表示,它表示光源先打到Specular也就是非常光滑的表面上,再打到Diffuse物体上再传到眼睛被看到。如上图的金属环。

通过观察我们可以发现任何的Transport path都可以被分成Lighting和Light transport两部分,也就是L和除L外的东西。也就是说无论Light transport多复杂我们都可以用PRT的方式进行预计算。

回顾一下Ti项的预计算,我们之前提到了可以把如图中所示的式子当作一种投影,而另一种理解思路,我们发现这个式子很像渲染方程,唯一不同的是Li变成了基函数,那我们可以就可以理解成用这些不同基函数形式的光照去照亮整个物体,如图所示,只不过每个光照会有些奇怪罢了,但是把它们合在一起,仍然是正确的完整的光照。(红色为+,蓝色为-,黑色为0) 

对于不同的BRDF,我们只要能把light transport表示出来,都可以通过预计算来实时渲染。即使为如上图所示的右下角的茶壶,不同的位置上有不同的BRDF,此时虽然任何一个顶点的BRDF仍是四维,但整个物体变成了六维的函数,但是我们仍然可以通过PRT计算。 

4.PRT的局限

⦿ 球谐函数只适合于描述低频的函数,描述高频要用很高阶的基函数

⦿ 因为预计算,所以只适用于静态场景。材质,场景都不能发生改变

⦿ 大量的预计算数据需要存储和读取0

二、Wavelet—小波

⦿ Wavelet

⦿ Zonal Harmonics

⦿ Spherical Gaussian (SG)

⦿ Piecewise Constant

事实上,在SH之后,人们研究了许多基函数,用来表示其他函数,如上面所列举的一部分。

我们简单介绍其中的一种,Wavelet—小波,并且小波有很多种,这里我们介绍的是2D Haar小波。与SH相同,它也是一系列基函数,但不同的是,SH定义在球面上,而它定义在图像块上,并且不同的小波定义域不同,如图中只有黑白的地方才是定义域,并且小波支持全频率的表示。其次,与SH不同的是,我们用SH近似的时候是取了SH有限阶的基函数去近似,而小波不同,我们把函数投影到小波的每个基函数上,会发现有些基函数的系数接近0,这样我们就可以定义一个系数大小,小于一定值的基函数丢掉就可以了。

自然的,由于小波定义在图像块上,那自然不能用Sphere map来做光照,而是改用Cubemap,并且对Cubemap的每张图单独做小波变换,大致思路为一张图划分为四块,左上角存储低频信息,然后其余为高频信息的小波变换保留下来的非0系数项,接着依次不断划分,如上图所示。

可以看到,小波还原出来的效果比SH好一些,包括高频的阴影。

但小波有一个缺点,就是它不支持光源的旋转,而不像SH的简单的旋转性质。 

三、Real-Time Global Illumination (in 3D)

[Ritschel et al., The State of the Art in Interactive Global Illumination]

全局光照在真实感渲染中有着举足轻重的作用,如果没有全局光照,场景中会出现许多死黑的地方,所以做全局光照是必然的,回顾我们在GAMES101里的Blinn-Phong模型,它的全局光照是做法是把间接光照ambient当成一个常数,然后假设场景所有地方所受间接光相同并且和Normal也没有任何关系,但显然,这是非常不准确的做法。 

[Image courtesy of Prof. Henrik Wann Jensen]

在实时渲染中,人们指的全局光照中的所谓间接光照,指的就是光线比直接光照多弹射一次的间接光照,而不是弹射很多次的,如上图所示。

Reflective Shadow Maps (RSM)

(1)Idea

[Image courtesy of Prof. Henrik Wann Jensen]

那么回顾我们在GAMES101里面提到的,在P点考虑接受的光照,有从光源接收到的的直接光照那就是光源的直接光照,有从Q点接收到的间接光照,那也就是Q点接收光源的光后反射到自己的光就好像Q点也是一个光源一样。

所以实际上我们并不区分哪些是直接照到的,哪些是反射来的,而是把Q当作次级光源来计算。

如上图所示,太阳标记表示被直接光照照到的部分,它们在下一次弹射的时候将被当作次级光源去照亮其它物体。 

如上图,p点接收不到光源的直接光照,但可以接收到第一次获得的次级光源的光照,然后被照亮。

(2)Key Observations 

有了思路,我们需要解决中间的一些问题

• 首先,我们怎么知道光源直接照到了哪些地方呢?

显然我们一下就可以想到借助Shadow Map。这个时候从Shadow Map上的每个像素对应的场景中的Area都会被当成次级面光源去照亮点p。

• 其次,我们怎么知道一个次级面光源对着色点(点p)的光照贡献呢

对光源直接采样

我们想到在GAMES101介绍蒙特卡洛积分对半球采样的时候,我们当时说因为会导致浪费许多路径,所以我们改写了渲染方程,把对立体角的积分改成了对光源上面积dA的积分,直接采样光源避免了浪费。而在这里我们同样可以采取这种方法,在p点对所有有贡献的次级光源采样即可。

但这里有一个问题,我们在p点求各个次级光源的贡献,实际上我们是以p点作为了观察点,而不是我们的Camera,所以我们如果不知道出射方向,那就没法计算着色了。但是这里我们会做一个假设,我们假设所有的反射物(reflector)都是Diffuse的,这样就和观测方向没有关系,我们从p点还是从Camera看都是相同的。但注意,我们不需要假设接收物都是也是Diffuse的,也就是说虽然次级光源被我们认为是Diffuse,但是点p并不需要认为是Diffuse的。

对光源面积积分的渲染方程

如上图所示是我们之前提到的改写后的渲染方程,从立体角积分变成了对面积的积分,其实如果我们如果知道了Shadow Map上的一个像素对应的面积就可以直接计算了,不需要积分dA。 现在需要知道的就是,从q点打到p点的Radiance是多少,也就是上图所示的Li(q→p)项。

由于之前假设的q点是Diffuse的,那么q点的BRDF就很好求,就是ρ/π ,我们之前推导过。那出射的Radiance自然就是BRDF乘以Irradiance,Irradiance直接用光源的Φ除以单位面积dA就可以了。并且dA可以消掉,于是得到最终的公式结果如上图白框所示。(这里的白框里的公式是Paper的原公式,可以看到闫令琪老师非常自信的把它的4次方改成了2次方,并且说Paper上绝对写的是错的立flag吃键盘,据闫令琪老师说这个错的原因在于无脑加了一次平方衰减,而实际上这个衰减的假设是错的但是实际上Paper里的是正确的,也就是说按照Paper原文这么写确实是4次方,因为分子结果上面有个2次方消掉了,当然闫令琪老师的推导也没有错,究其原因是分子不同导致分母不同。)

这里仍然存在这问题,首先我们发现改写后的渲染方程的Visibility项无法得到,我们不可能对每个次级光源和着色点之间都生成一张Shadow Map来判断可见性,于是人们直接放弃了Visibility项,就认为是可见的。

(3)Tips

RSM仍然有一些可以优化的点,比如我们之前提到的次级光源的Visibility项,以及根据法线判断哪些光源根本不可能对着色点有贡献,比如上图的在桌子上的次级光源(x-1,x-2)就不可能对x点有贡献。其次我们之前看到了,改写渲染方程有距离平方衰减项,所以一定距离范围外的次级光源我们自然也没必要考虑,因为太小了。

离着色点一定距离范围外的次级光源不考虑?我们自然不能对每个着色点p把Shadow Map上所有次级光源遍历计算距离再排序,再删除,这是很大的工程量,于是有了RSM另外一个大胆的假设。

我们目的是找到在世界坐标下距离着色点p距离近的一些点。于是我们把p点投影到Shadow Map上,然后在Shadow Map上离点p近的像素/深度接近的像素,我们就认为在世界坐标下距离也接近。这样就可以有效的加速这一过程。当然查询可能仍然很慢,我们可以随机采样,至于采样点的选取,采样点的数量,采样的权重分布,这些都可以借鉴PCSS等之前提到过的其它方法,这里因实际情况而有所不同,但我们可以知道的是,这种方法可以有效加速RSM。

(4)Summary

RSM相比于普通的Shadow Map多存储了什么呢?那无非是我们之前说思路的时候用到的哪些信息。Depth深度,这是Shadow Map原本就存储的。除此之外还有,世界坐标来判断距离(算Shading的时候需要使用),反射物的法线(计算cos项),光源的辐射通量。

GDC Vault - In-Game and Cinematic Lighting of The Last of Us

RSM的效果被用在手电筒上非常不错,而且因为手电筒覆盖范围较小,不需要太大的RSM,开销比较小 。

RSM的优点是比较容易实现,因为它本身其实就是Shadow Map。

那缺点也很容易想到,Shadow Map有的缺点它也有。有多少个直接光源就需要多少个RSM,所以多光源开销大。 其次我们说了,无法考虑Visibilty项,于是不考虑,所以不够真实。还有,RSM做了很多假设包括:反射物都是Diffuse,SM近似的世界空间坐标距离,这些都会对渲染质量有一定影响。最后RSM的采样率和质量的平衡问题,这是所有采样方法都有的问题。

参考

GAMES202_Lecture_07 (ucsb.edu)

Lecture7 Real-time GLobal Illumination (in 3D)_哔哩哔哩_bilibili

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

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

相关文章

PHP8函数包含文件-PHP8知识详解

在php中&#xff0c;可以使用以下函数来包含其他文件&#xff1a;include()、include_once()、require()、require_once()。 1、include(): 包含并运行指定文件中的代码。如果文件不存在或包含过程中出现错误&#xff0c;将发出警告。 <?php include filename.php; ?>…

【mybatis-plus进阶】多租户场景中多数据源自定义来源dynamic-datasource实现

Springbootmybatis-plusdynamic-datasourceDruid 多租户场景中多数据源自定义来源dynamic-datasource实现 文章目录 Springbootmybatis-plusdynamic-datasourceDruid 多租户场景中多数据源自定义来源dynamic-datasource实现0.前言1. 作者提供了接口2. 基于此接口的抽象类实现自…

macOS通过钥匙串访问找回WiFi密码

如果您忘记了Mac电脑上的WiFi密码&#xff0c;可以通过钥匙串访问来找回它。具体步骤如下&#xff1a; 1.打开Mac电脑的“启动台”&#xff0c;然后在其他文件中找到“钥匙串访问”。 2.运行“钥匙串访问”应用程序&#xff0c;点击左侧的“系统”&#xff0c;然后在右侧找到…

Gin学习记录3——模版与渲染

模版与渲染 一. 返回二. 模版2.1 基础模版2.2 同名模版2.3 模版继承2.4 模版语法 一. 返回 如果只是想返回数据&#xff0c;可以使用以下函数&#xff1a; func (c *Context) JSON(code int, obj any) func (c *Context) JSONP(code int, obj any) func (c *Context) String(…

Shotcut for Mac:一款强大而易于使用的视频编辑器

随着数码相机的普及&#xff0c;视频编辑已成为我们日常生活的一部分。对于许多专业和非专业用户来说&#xff0c;找到一个易于使用且功能强大的视频编辑器是至关重要的。今天&#xff0c;我们将向您介绍Shotcut——一款专为Mac用户设计的强大视频编辑器。 什么是Shotcut&…

POI基于Excel模板导出数据

1、基于模板导出列表数据 1.1、需求 注意&#xff1a;使用附件的形式下载&#xff0c;前端访问必须通过window.open(),否则附件可能无法下载。 按照以下样式导出excel 1.2、思路 首先准备一个excel模板&#xff0c;这个模板把复杂的样式和固定的内容先准备好并且放入到项…

佳作导读 | 《C++ Core Guidelines》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 佳作导读 | 《C Core Guidelines》 《C Core Guidelines》由Bjarne Stroustrup和Herb Sutter等共同编写关于使用C编程语言的指南&#xff1b;旨在提供关于如何使用C进…

在Ubuntu上安装CUDA和cuDNN以及验证安装步骤

在Ubuntu上安装CUDA和cuDNN以及验证安装步骤 本教程详细介绍了如何在Ubuntu操作系统上安装CUDA&#xff08;NVIDIA的并行计算平台&#xff09;和cuDNN&#xff08;深度神经网络库&#xff09;&#xff0c;以及如何验证安装是否成功。通过按照这些步骤操作&#xff0c;您将能够…

小黑受到了未来的焦虑,周四继续参加团跑活动仰山跑,跑奥森的坡,越跑越上瘾更加热爱生活的leetcode之旅:LCR 008. 长度最小的子数组

小黑代码1 class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:# 数组长度n len(nums)# 双指针head 0tail 0# 中间变量sum_ 0# 结果变量res n1# 开始双指针迭代while tail < n:sum_ nums[tail]tail 1while sum_ > target:if tail…

0010Java程序设计-springboot+vue影院售票系统设计与实现

摘 要目 录系统实现开发环境 摘 要 看电影已经成为了人们生活中不可缺少的一部分&#xff0c;电影院售票及管理系统是电影院的日常管理及售票任务的核心&#xff0c; 在电影院中&#xff0c; 工作人员并非只是放映电影&#xff0c; 还有诸如票房统计、影片放映、影片场次安排、…

动态规划:路径和子数组问题(C++)

动态规划&#xff1a;路径和子数组问题 路径问题1.不同路径&#xff08;中等&#xff09;2.不同路径II&#xff08;中等&#xff09;3.下降路径最⼩和&#xff08;中等&#xff09;4.地下城游戏&#xff08;困难&#xff09; 子数组问题1.最大子数组和&#xff08;中等&#xf…

一篇文章教会你SpringMVC

目录 1.什么是SpringMVC 2.SpringMVC工作流程 3.SpringMVC核心组件 4.SpringMVC的配置流程 4.1导入POM依赖 4.2在WEB-INF下添加springmvc-servlet.xml(spring-mvc.xml) 4.3 修改web.xml 创建一个Controller用来存放web层的方法和内容 创建一个前端页面用来做测试展示 前言…

04 Linux补充|C/C++

目录 Linux补充 C语⾔ C语言中puts和printf的区别&#xff1f; Linux补充 (1)ubuntu安装ssh服务端openssh-server命令&#xff1a; ubuntu安装后默认只有ssh客户端&#xff0c;只能去连其它ssh服务器&#xff1b;其它客户端想要连接这个ubuntu系统&#xff0c;需要安装部署…

进制转换(二进制、八进制、十六进制、十进制)

一、进制表示 二进制&#xff1a;每一位只有两种符号表示 -> 0,1 例如 (101011)₂&#xff0c;也可写作101011B&#xff0c;其中B是Binary英文的缩写。八进制&#xff1a; 每一位有8种符号表示(0~7)&#xff0c;例如(1652)₈&#xff0c;也可写作1652O&#xff0c;其中O是O…

STL常用容器 (C++核心基础教程之STL容器详解)String的API

在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;有多种容器可供使用。以下是一些常见的容器类型&#xff1a; 序列容器&#xff08;Sequential Containers&#xff09;&#xff1a; std::vector&#xff1a;动态数组&#xff0c;支持快速随机访问。 std::list&…

CS420 课程笔记 P7 - 虚拟内存 多级指针寻址

文章目录 IntroPointersMemory leaksPointer pathPointer scanningExample! Intro 上节课我们学习了静态地址&#xff0c;这节课我们将着手关注动态地址&#xff0c;我们需要了解一个叫做指针的东西 Pointers 简单地说&#xff0c;指针是对象之间的单向连接 Pointers are co…

vue集成mars3d后,basemaps加不上去

首先&#xff1a; <template> <div id"centerDiv" class"mapcontainer"> <mars-map :url"configUrl" οnlοad"onMapload" /> </div> </template> <script> import MarsMap from ../component…

C到C++的升级

C和C的关系 C继承了所有C语言的特性&#xff1b;C在C的基础上提供了更多的语法和特性&#xff0c;C语言去除了一些C语言的不好的特性。C的设计目标是运行效率与开发效率的统一。 变化一&#xff1a;所有变量都可以在使用时定义 C中更强调语言的实用性&#xff0c;所有的变量…

解决centos离线安装cmake找不到OpenSSL问题

安装方法&#xff1a;见另外一篇文章 https://blog.csdn.net/zhongxj183/article/details/118488629 按照文章下载了离线gcc 和OpenSSL&#xff0c;以及在cmake官网下载了最新版 cmake-3.27.4.tar.gz 顺利安装gcc 和OpenSSL 但执行编译cmake时&#xff0c;报错找不到OpenSSL…

【python】读取.dat格式文件

import binascii# 打开二进制文件以只读二进制模式 with open(EXCEL/文件.dat, rb) as file:binary_data file.read()print(binary_data)# 将二进制数据转换为十六进制字符串 hex_data binascii.hexlify(binary_data).decode(utf-8) # binary_data 现在包含了文件的二进制内容…