Q135:PBRT-V3,随机渐进光子映射(Stochastic Progressive Photon Mapping)(16.2章节)

一、概述

一开始出现的是Photon Mapping,后来有了Progressive Photon Mapping,后来才有了Stochastic Progressive Photon Mapping。
关于这三个的前世今生等相互关联,参考:
photon mapping学习笔记
再谈光子映射

前面的都不是重点。
咱关注的是PBRT-V3上介绍的SPPM(Stochastic Progressive Photon Mapping)的原理及其C++代码实现。

二、SPPM的原理

SPPM是PPM的改版(或者说“升级版”)。SPPM的优势是没有内存的限制。

SPPM算法有如下几个步骤:

1,由相机产生visible points(“visible point”,即“(对相机来说)可见的点”。基本是一个像素点对应一个visible point,所以很省内存。这个过程中会计算visible point的“直接光照”)。
2,将所有visible points保存到一个grid中。
3,由光源发出光子光线给grid中的visible points“送光”。
4,步骤1、2、3结束,意味着一次迭代将要结束,将要开始下一次迭代。在开始下一次迭代之前,根据当前迭代的情况调整下一次迭代光子“送光”是的搜索半径。然后,回到步骤1开始下一次迭代。

5、所有迭代完成后,计算每一个visible point最终的“间接光照”,在“直接光照”的基础上添加“间接光照”,输出图形。

2.1 产生visible points

前面已经提到“visible points”是指“对相机来说,可见的点”。
那么,场景中那些点对相机来说是可见的呢?
从相机产生一条光线进入场景,“visible point”指的是:
1,光线直接diffuse surface相交的交点;
2,光线发生specular (reflection/transmission) 之后,和diffuse surface相交的交点。

这里写图片描述

一般情况下(即,不考虑光线在specular surfaces之间弹来弹去和打出场景),一个像素点,对应一条光线,对应一个visible point。

保存visible point的数据:

这里写图片描述

其中,特别说明一下Ld、radius。

Ld,visible point处的直接光照;

radius,visible point的“搜索半径”/“收光半径”。对于每一个visible point,当前只保存了“直接光照”。后续,来自光源的光子光线给每个visible point送来的光是属于“间接光照”。光子光线“送光”的方式:光线和场景中某surface相交产生一个交点,该交点附近的所有visible points都有可能收到当前光子光线送来的光,只要visible point到该交点的距离小于或者等于该visible point的“搜索半径”/“收光半径”。

2.2 构建grid

grid的每个cell中保存着一个visible point链表。

前面提到,每一个visible point都有自己的“搜索半径”/“收光半径”。
这个就相当于一个“以visible point为球心,以“收光半径”为半径“的球。
visible point对应的这个球可能和grid中的多个cell发生重叠,所以需要将这个visible point添加到有重叠的多个cell的“visible point链表”中。

另外,可能存在多个visible point对应的球面和同一个cell发生重叠,所以,一个cell的“visible point链表”中会保存所有“对应球面和该cell发生重叠”的visible point。

这里写图片描述

2.3 光子光线“送光”

来自光源的光子光线和场景中的某surface相交,得到一个交点。
将该交点坐标转换到“前面根据visible point构建”的grid中。
该交点经过转换后会对应grid中的某个cell。
该cell中保存了一个visible point的链表。
这条光子光线将给这个cell对应的visible point链表中的所有visible points“送光”。光子光线给visible point送来的光即为visible point处的“间接光照”。

光子光线“送光”是这样的:送完一家,送另一家。

这个有点类似于path tracer中path的构建。

这里写图片描述

关于“送光”的量,这里也是用beta表示(这个beta和visible point中保存的那个beta的物理意义是不一样的。两个beta在程序中的作用范围不一样,所以不会冲突)。
初始beta(即光子光线对应path的长度为1时):
这里写图片描述
(这里面涉及到光子光线的采样,参考16.1.2章节,此处不表)

随着光子光线对应path的长度的增加,beta值发生改变。
这里涉及到一个问题:光子光线不能是无止尽地在场景中延伸,那么怎么终止光子光线呢?
一个光子撞击物体后,是继续传播呢,还是被物体吸收呢?这个用Russian roulette来决定。
β的计算如下:
这里写图片描述

2.4 调整visible point的“搜索半径”

对于每一迭代(对应若干条光子光线),相关数据的计算如下放截图:

这里写图片描述

当最后一次迭代结束时,计算光源给某个visible point送光所产生的效果(visible point的间接光照):

这里写图片描述

正因为“搜索半径”的存在,所以SPPM是一个biased的算法。
当然,实际使用中,随着迭代次数的增加,“搜索半径”越来越小,从而biased的程度越来越小,得到的图形也越来越精确。

2.5 关于单次迭代的光子数和迭代次数

直接看张图吧:

这里写图片描述

如a图,单次迭代的光子数为10000个,迭代1000次。
(关于单次迭代的光子数,PBRT-V3官方代码中做法:若用户有设置单次迭代的光子数,就用这个数值;若用户没有设置单次迭代的光子数,就把像素点的个数作为单次迭代的光子数的默认值。
关于迭代次数,由于不涉及内存问题,可由用户随意设置)

三、SPPM的C++代码实现

PBRT-V3中对应的积分器是SPPMIntegrator。
SPPMIntegrator不是SamplerIntegrator,所以要实现自己的render()成员方法。

先截取需要特别说明的代码段,在贴出完整的render()成员方法。

产生visible point时:

这里写图片描述

这里写图片描述

给visible point送光时:

这里写图片描述

这里写图片描述

这里写图片描述

调整visible point的“搜索半径”时:

这里写图片描述

给visible point添加最终的间接光照时:

这里写图片描述

完整render()代码如下:

// SPPM Method Definitions
void SPPMIntegrator::Render(const Scene &scene) {
    ProfilePhase p(Prof::IntegratorRender);
    // Initialize _pixelBounds_ and _pixels_ array for SPPM
    Bounds2i pixelBounds = camera->film->croppedPixelBounds;
    int nPixels = pixelBounds.Area();
    std::unique_ptr<SPPMPixel[]> pixels(new SPPMPixel[nPixels]);
    for (int i = 
### 关于 pbrt-v3 中 LESS 文件或样式相关内容 pbrt-v3 是一个专注于物理基础渲染的开源项目,其核心目标是实现高质量的光线追踪渲染效果。然而,在该项目中并未涉及前端开发中的 CSS 预处理器(如 LESS 或 SASS),因为这些工具主要用于网页设计和前端界面开发领域[^1]。 #### 1. pbrt-v3 的文件结构分析 pbrt-v3 主要由 C++ 编写而成,源码目录按照功能模块进行了划分。例如,`src/core` 存放核心算法逻辑,而 `src/integrators` 则定义了不同的光照积分器实现方式。如果尝试寻找与样式相关的文件,则需注意以下几点: - **LESS 文件用途**:LESS 是一种动态样式表语言,扩展了标准 CSS 功能,允许变量、嵌套规则等功能。这种技术通常用于 Web 开发环境下的 UI 设计。 - **pbrt-v3 不适用场景**:由于 pbrt-v3 并未提供图形化用户界面(GUI),也无需处理 HTML/CSS 渲染问题,因此不会存在任何 LESS 文件或其他类似的样式资源[^4]。 #### 2. 如果需要自定义可视化风格怎么办? 尽管官方版本不支持 GUI 和样式定制,但开发者可以借助第三方库来增强用户体验。以下是两种可能的方向: - 使用 Qt 创建跨平台桌面应用程序接口; - 将渲染结果导出至图像文件并通过外部框架展示出来。 对于上述需求之一——即通过 Qt 实现带样式的交互窗口——则确实需要用到一些类似于主题设置的概念。不过这属于二次开发范畴,并不在原始代码库范围内[^2]。 ```cpp // 示例:假设我们想基于 Qt 构建简单的查看器 #include <QApplication> #include <QWidget> int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; window.resize(800, 600); // 设置大小 window.setWindowTitle("Custom Styled Viewer"); // 添加标题 QString styleSheet = "background-color: #f0f0f0; color: black;"; window.setStyleSheet(styleSheet); window.show(); return app.exec(); } ``` 此片段展示了如何利用 stylesheet 属性改变控件外观属性。当然实际应用时还需要进一步集成到整个工作流当中去[^3]。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值