计算机图形学入门23:蒙特卡洛路径追踪

1.前言

        前面几篇文章介绍了Whitted-style光线追踪,还介绍了基于物理渲染的基础知识,包括辐射度量学、BRDF以及渲染方程,但并没有给出解渲染方程的方法,或者说如何通过该渲染方程计算出屏幕上每一个坐标的像素值。

        Whitted-style光线追踪是存在很多不正确的情况,但是基于渲染方程是正确的。本文就介绍通过蒙特卡洛路径追踪来解渲染方程的方法。

2.蒙特卡洛积分

        蒙特卡洛积分(Monte Carlo Integration)是为了解决函数的定积分。给任意函数要算出这个函数的定积分,而定积分是一个数值。下面举个例子,如下图所示,如何去求曲线在a、b之间围成的面积。

        求曲线在a、b区域内面积也就是求曲线的函数在[a, b]内的定积分。但对于这样一个曲线,很难用一个数学函数去表示,因此无法用一般解析的方法直接求得积分值,可以采用黎曼积分的思路。

        也就是对上图这个图形纵向均匀地切割,分成多个宽度相同的条形,那么这些条形就可以近似为长方形,取条形中线的高度作为近似长方形的高度,将这些近似小长方形的面积全部加起来就能得到整个图形近似的面积。

        当切割的数量越多也就是采样越多,得到的结果越逼近真实的数值。

        而相比起黎曼积分的均匀采样,蒙特卡洛积分可以指定一个随机分布来对被积分的值进行采样,因此更加通用,定义如下。

        如上图所示,我们希望求出一个函数f(x)在积分域[a,b]上的积分值,选定一个采样的分布p(x),通过对该分布来进行多次的函数值采样,最后近似的积分值如图中最下方式子所示。

        注意公式中的1/N可以理解为不将图形分割,而是每次采样都近似为一个采样对应的函数值的长方形,将多次采样近似的长方形面积累加求均值,如下图是采样次数为4时的。

        这里对前面的公式进行一个简单的推导,从概率论的角度理解,求均值的做法其实也是对期望的逼近。

        那么对于这样一个服从某一分布的期望的计算套公式直接计算得。

        通过以上推导即可明白蒙特卡洛的近似正是对积分值的一个无偏估计,也就是采样次数越多,近似值与积分值越接近。简单说就是样品越多,反差越小

        为了方便,一般都使用均匀采样,因此很容易推出。

        因此,蒙特卡洛就是对函数值进行多次采样求均值,从而帮助求得困难积分值的近似的方法

3.蒙特卡洛路径追踪

3.1 Whitted-style光线追踪的问题

          在前面的文章中学过Whitted-style光线追踪主要是不断在弹射光线,那么是怎么弹射光线的?

        1.当光线打到表面为光滑的物体上,比如玻璃,那么光会沿着镜面方向反射或者沿着折射方向折射。

        2.当光线打到漫反射物体上,光线就停了。

        那么,这两个简化是对的吗?

        其实,这个不一定是对的,下面举例了几个光线追踪的问题。那么,就有了路径追踪(Path Tracing)来解决很多基于非物体或者不正确的问题。

        1.whited-style光线追踪对于反射表面进行镜面反射,难以模拟介于漫反射和镜面反射之间的光泽反射(Glossy Reflection)。

        对于有光泽的材料,光线应该反射到哪里?如下图所示。

        2.whited-style光线追踪对于漫反射表面直接进行光照计算,并没有对漫反射的光线进行追踪。

        whited-style光线追踪把光线打到漫反射物体上,光线就停了,但是实际上还是有光线会反射不同方向上去。如下图所示,直接光照与全局光照的区别,全局光照图上可以看到红色和绿色墙面的颜色反射到了中间的物体表面上。

3.2 解渲染方程

3.2.1 解渲染方程的难点

        上面介绍了whited-style光线追踪存在的问题,但是渲染方程是正确的,回顾渲染方程使用了各种辐射度量学、BRDF提供标准来推导出来的,方程如下。

        要想解出渲染方程的解主要有两个难点:

        1.半球积分的计算。考虑来至四面八方的光照,就要求整个半球(不考虑背面光)的积分。

        2.递归形式。反射来的光线有可能是直接光照,也可能是其他物体反射来的间接光照。

        对于第一个问题,考虑到如何用一个数值来解一个积分,就需要用到前面所学的蒙特卡洛积分了。

3.2.2 一个简单的蒙特卡洛解决方案

        假设我们在以下场景中渲染一个像素(点),仅用于直接光照,场景中为面光源,如下图所示。其中ω0为眼睛的观察方向,ωi则是各个方向弹射到点 的光线(这里箭头方向与光线实际方向相反是因为渲染方程中所有方向都以表面向外为正方向)。

        先对渲染方程稍微做一下滥用,舍弃自发光项(假设除了光源其他物体不会发光),从而方便进行计算推导如下。由于限定了直接光照,所以四面八方来的光只有光源自身发射的。其物理含义为着色点 到摄像机或人眼的Radiance值。

        这个方程看起来很复杂,其实就是一个半球在不同方向上的积分,所以就用蒙特卡洛积分来求值

        回想前面所提到的,对于一个困难积分只要选定一个被积分变量的采样分布即可通过蒙特卡洛的方法得到积分结果的近似值。而此时的被积分值为入射光线方向ωi,选定采样分布p(ωi)为在 点的半球方向内对ωi进行均匀取样(只考虑从平面上方入射的光线,半球的立体角为2π,p(wi)为1/2π),不难得出积分近似结果如下:

        到此时可以算出任何一个着色点出射的Radiance。对这个着色点 p 出射方向为w_{o},要计算来至四面八方光线对这个点直接光照的贡献,就可以写出一个算法,如下伪代码所示。

        L_i 代表光源亮度(Radiance),f_r 代表BRDF,pdf(wi) 代表均匀采样的值。

3.2.3 全局照明

        上述只考虑直接光照显然是不够的,还需要间接光照,即当采样的ωi方向碰撞到了别的物体,如下图所示。

        此时采样的光线ωi碰撞到了另一个物体的Q点,如果这条光线在Q点反射后没有碰到光源,这条路径自然没有贡献;如果光线在Q点反射后碰到了光源,那么该条路径对着色点P的贡献是多少呢?

        此时可以理解为ωi方向为Q点的观察方向,求Q点到ωi方向的Radiance值,也就是光源在点Q的直接光照再乘上Q点发射到ωi方向的比例。

        显然这是一个类似光线追踪的递归过程,不同点是该方法通过对光线方向的采样从而找出一条条可以连接眼睛和光源的的光线路径,这也正是为什么叫路径追踪的原因,伪代码如下:

        在Q点反射过来的Radiance,就相当于在Q点反射过来的直接光照,也就是在Q点从-wi方向看过去的直接光照。这里是-wi方向是因为从P点到Q点是wi方向,那么从Q点到P点自然就是-wi

        至此,我们成功通过蒙特卡洛的方式解出(近似)了渲染方程的积分值,也通过考虑直接光照与间接光照解决了递归的问题。到此,是否已经解决了?

3.2.4 路径追踪(Path Tracing)

        接上述疑问,还有两个问题。

        1.随着反射增加,射线爆炸

        如下图所示,我们通过每次对光线方向的采样从而解出方程,假设有一条光线打到物体上,为了计算直接光照,假如从物体上打出100条光线,那么100条都有可能打到其他物体上,在其他物体上又要算直接光照,那么每条再发射100条光线,就是10000条,依次类推,反射越多次光线数量便会指数级爆炸增长,计算量完全无法接受,那么如何才能使得光线数量不爆炸增长呢?

        唯有每次只采样一个方向,也就是N = 1(1的多少次方都是1)。N = 1时就是通常所说的路径追踪(连接一条视点与光源的路径),作为区分N != 1时,一般称为分布式路径追踪

        从现在开始,我们总是假设每个着色点只有一条光线被追踪,伪代码如下。

        如果只采样一个方向那么所带来的问题也是显而易见的,积分计算的结果会非常的随机,虽然蒙特卡洛积分是无偏估计,但样本越少显然偏差越大。

        但该问题也好解决,如果每次只去寻找一条路径结果不准确,那么就去寻找多条路径。如下图所示,分别有红、蓝、黑三条路径。

        从摄像机向每个像素的区域内发射多条路径,追踪每条路径的反射并计算,最后将多条路径的结果求平均即可。伪代码如下。

        到此解决了射线爆炸问题,但还没有彻底解决,因为shade函数的递归没有出口,永远不会停下。

        因为在现实中光线是经过无数次反射的,如果在这里给光线追踪算法中设定最大反射次数的方式,这样就会导致能量损失,所以这里非常精妙的采用了俄罗斯轮盘赌(Russian Roulette)

       一把弹仓6发的左轮手枪,随机填充两发子弹,按下扳机时,目标有4/6的概率活下来,这就是俄罗斯轮盘赌的概念。

        假设一个着色点出射的结果为Lo,期望递归停止后返回的结果还是Lo。那么,首先设定一个概率P(0<P<1),每次光线反射时有P的概率光线会继续递归并设置返回值为Lo/P,有1-P的概率光线停止递归,并返回0。这样巧妙的设定之下光线一定会在某次反射之后停止递归,并且计算的结果依然是无偏的,因为从概率论的角度来看,Radiance的期望Lo不变,证明如下。

        shade函数的伪代码变更如下。

        至此,路径追踪算法已经完成大半,只差最后一个小问题!

        2.现在的路径追踪效率非常的低下

        如下图所示,图中顶部为面光源,中间为遮挡物。左图表示每个像素使用更少的采样(Low SPP),右图是每个像素使用更高的采样(High SPP)。可以发现采样点更多,效果越好,越干净(噪点少),所以效率很低,最好在Low SPP情况下也能达到很好的效果。有没有办法呢?

        其实是有办法提高效率的,如下图所示就是造成低效率的原因。

        在每次计算直接光照的时候,通过均匀采样随机方向打出光线,但只有极少数的光线方向可以碰到光源,尤其当光源越小的时候,这种现象越明显,大量采样的光线都是无效浪费的。

        蒙特卡洛方法允许任何采样方法,不仅限于均匀采样。因此我们可以直接对光源进行采样(如果没有别的物体遮挡,光线没有被“浪费”)。

        假设光源的面积为A,那么对光源进行的采样分布p(A) = 1/A (因为光源面积p(dA)积分起来为1),但原始的渲染方程:

        很明显是对光线方向ωi进行积分的,如果想要对光源进行采样并依然使用蒙特卡洛的方法,那么一定要将其修改为对光源面积 dA 的积分(改变积分域),换言之就是需要找到 dA 与 dω 的关系。如下图所示。

        dA 与 dω 的关系如下。

        关系式中的 cosθ’ 是为了计算出光源上微分面积元正对半球的面积,之后再按照立体角的定义dω = dA / rr,即除以着色点x与光源采样点 x’ 距离的平方。

        于是根据图中二者的关系可将渲染方程改写如下。

        这样便成功从对 ωi 的积分转到了对光源面积 A 的积分,然后就可以利用蒙特卡洛的方法对光源进行采样从而计算直接光照的积分值,因为是对光源进行采样所以就不需要俄罗斯轮盘赌了(不涉及弹射)。对于间接光照,依然采用先前的方法进行光线方向的均匀采样。最终伪代码如下,分直接光照和间接光照两部分计算。

        计算直接光照的时候还需要判断光源与着色点之间是否有物体遮挡,该做法也很简单,只需从着色点 向光源采样点 x’ 发出一条检测光线判断是否与光源之外的物体相交即可,如图所示。

        伪代码修改如下。

        到这一步,路径追踪才真正完整了,路径追踪几乎可以达到100%真实感,如下图所示真实照片和路径追踪效果的对比,感受一下照片级真实(Photo-Realistic)。

3.2.5 遗留问题

        1.路径追踪对点光源很难处理。只能把点光源做成面积很小的面光源。

        2.路径追踪实现很困难。需要掌握的知识太多,比如物理量、微积分、概率、几何。

        

        

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

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

相关文章

SQLServer:从数据类型 varchar 转换为 numeric 时出错。

1.工作要求 计算某两个经纬度距离 2.遇到问题 从数据类型 varchar 转换为 numeric 时出错。 3.解决问题 项目版本较老&#xff0c;使用SQLServer 2012 计算距离需执行视图&#xff0c;如下&#xff1a; SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GO ALTER view vi_ord…

【坚果识别】果实识别+图像识别系统+Python+计算机课设+人工智能课设+卷积算法

一、介绍 坚果识别系统&#xff0c;使用Python语言进行开发&#xff0c;通过TensorFlow搭建卷积神经网络算法模型&#xff0c;对10种坚果果实&#xff08;‘杏仁’, ‘巴西坚果’, ‘腰果’, ‘椰子’, ‘榛子’, ‘夏威夷果’, ‘山核桃’, ‘松子’, ‘开心果’, ‘核桃’&a…

CTO透露GPT-5内幕,OpenAI 以36亿美元收购数据库初创公司

目录 01 GPT-5 02 OpenAI收购Rockset 2.1 谁是Rockset&#xff1f; 2.2 OpenAI的目的是什么&#xff1f; 01 GPT-5 虽然GPT-4的视频通话功能尚未全面推广&#xff0c;但OpenAI的CTO已经对即将到来的GPT-5给出了新的暗示。 不久前&#xff0c;Mira回到母校达特茅斯工程学…

springboot 自定义的全局捕获异常失效

背景&#xff1a;springbootspringcloud 分布式微服务。 问题&#xff1a;公共模块在使用RestControllerAdvice全局捕获异常时&#xff0c;捕获不到子服务抛出的相应异常 首先看一下全局异常组件有么有被扫描到 如何查看&#xff0c;很简单只需要写一段类加载打印代码&#x…

剪映数字人口播原理终于搞清楚了

剪映版本升级了,新版本支持数字人定制,于是我赶紧申请了使用资格 目前的价格是49元单个价格/30天 支付49元之后剪映要求上传2.5至10分钟的视频 接着要阅读一段话并录制视频上传 第三步提交,提交完成之后大概两三个小时就会有一个特定数字人形象出现:

嵌入式c语言3——自定义数据类型

结构体struct&#xff0c;共用体union 结构体中定义变量&#xff0c;首尾地址相连 对于union&#xff0c;其包含变量对起始地址相同 由于其起始地址相同&#xff0c;则改变其中某一变量值时有可能使得另一个变量值发生改变 enum 枚举&#xff0c;可以用来定义一堆整形常量构成…

深入详解RocketMQ源码安装与调试

1.源码下载 http://rocketmq.apache.org/dowloading/releases/ 2. 环境要求 64位系统JDK1.8(64位)Maven 3.2.x

性价比蓝牙耳机怎么选?百元高性价比蓝牙耳机推荐

在现代社会中&#xff0c;蓝牙耳机已经成为人们日常生活中必不可少的配件之一。对于许多消费者来说&#xff0c;找到一款高性价比且价格在百元左右的蓝牙耳机是非常重要的。市面上有许多价格不菲的蓝牙耳机&#xff0c;性价比蓝牙耳机怎么选&#xff1f;如何在有限预算下找到性…

ODN网络弱光聚类定界与整治

01 ODN网络弱光运维现状 ODN网络是家庭宽带连接系统-无源光网络 (PON) 的重要组成部分&#xff0c;是连接局端 OLT 和用户 ONT 之间的光路通道&#xff0c;其质量直接影响整个PON系统的性能及可靠性。ODN光纤链路包括OLT PON口、ODF、主干光纤、一级分光器、分支光纤、二级分光…

Unity Shader技巧:实现带投影机效果,有效避免边缘拉伸问题

这个是原始的projector 投影组件,边缘会有拉伸 经过修改shader 后边缘就没有拉伸了 (实现代码在文章最后) 这个着色器通过检查每个像素的UV坐标是否在定义的边界内,来确定是否应用黑色边框。如果UV坐标处于边缘区域,那么像素颜色会被强制设为黑色,从而在投影图像周围形成一…

240703_昇思学习打卡-Day15-K近邻算法实现红酒聚类

KNN(K近邻)算法实现红酒聚类 K近邻算法&#xff0c;是有监督学习中的分类算法&#xff0c;可以用于分类和回归&#xff0c;本篇主要讲解其在分类上的用途。 文章目录 KNN(K近邻)算法实现红酒聚类算法原理数据下载数据读取与处理模型构建--计算距离模型预测 算法原理 KNN算法虽…

日产X-Trail | 压电式喷油器故障

故障现象 冷启动正常&#xff0c;但超车或者发动机处于负荷状态时就会出现熄火。熄火后无法重新发动&#xff0c;要等发动机完全冷却下来才能再次启动。 有两个故障代码&#xff1a;“P2146&#xff1a;喷油器1和2电源对地短路” 和 “P2146&#xff1a;喷油器3和4电源对地短…

vscode python调试,找不到控制调试工具栏,被隐藏了

问题&#xff1a; 如图所示&#xff0c;最开始蓝框中的调试台被莫名其妙的隐藏了&#xff0c;没法进行调试。 解决办法&#xff1a; 打开设置输入调试点击调试&#xff08;31&#xff09;找到红框选的那个选项&#xff0c;选择floating

Django QuerySet对象,all()方法

all()方法 在Django中&#xff0c;all()方法是QuerySet对象的一个方法&#xff0c;用于获取模型的所有实例。 当你调用ModelName.objects.all()时&#xff0c;Django会生成一个SQL查询&#xff0c;从数据库中获取该模型的所有记录&#xff0c;并返回一个QuerySet对象&#xf…

匠心独运:红酒与手工艺的很好结合

在岁月的长河中&#xff0c;红酒与手工艺都以其不同的魅力和技艺&#xff0c;书写着各自的故事。当这两者相遇&#xff0c;仿佛是一场跨越时空的对话&#xff0c;不仅展现了匠心独运的技艺之美&#xff0c;更在无声中诉说着对品质与生活的热爱。今天&#xff0c;就让我们一起探…

echarts-wordcloud:打造个性化词云库

前言 在当今信息爆炸的时代&#xff0c;如何从海量的文本数据中提取有用的信息成为了一项重要的任务。词云作为一种直观、易于理解的数据可视化方式&#xff0c;被广泛应用于文本分析和可视化领域。本文将介绍一种基于 echarts-wordcloud 实现的词云库&#xff0c;通过其丰富的…

魔行观察-AI数据分析>>勒泰中心购物中心

摘要 本报告基于 魔行观察 搜集整理的数据&#xff0c;对勒泰中心购物中心的营业状态、商户构成、业态分布以及消费者评价进行了详细分析。 商场概览 勒泰中心是一个正常营业的购物中心&#xff0c;自2013年开业以来&#xff0c;已成为当地居民和游客的重要购物和休闲场所。…

golang写的自动更新器

文件自动更新器&#xff0c;这个很多端游和软件都有用到的。 golang的rpc通信&#xff0c;是非常好用的一个东西&#xff0c;可以跟调用本地函数一样&#xff0c;调用远程服务端的函数&#xff0c;直接从远程服务端上拉取数据下来&#xff0c;简单便捷。 唯一的遗憾就是&#x…

Speculative decoding 投机采样原理和验证

概念 做法有很多&#xff0c;这里介绍最广泛被人认识的一种&#xff08;朴素投机采样&#xff09; 瓶颈&#xff1a; 大模型推理自回归采样&#xff0c;逐步串行解码。 生成的每个Token都需要将所有参数从存储单元传输到计算单元。 因此&#xff1a;内存访问带宽成为重要的瓶颈…

python gdal 压缩栅格数据

1 压缩方法LZW 使用 LZW&#xff08;Lempel-Ziv-Welch&#xff09;&#xff0c;主要对图像数据压缩&#xff0c;可逆 2 代码 函数gdal_translate()&#xff1a;转换栅格的不同格式 我们使用的数据是GTiff格式的数据 GTiff – GeoTIFF File Format — GDAL documentation 参…