Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Photon Mapping 大作业报告

Photon Mapping (光子映射) 算法是一种全局光照渲染算法,能够处理Path Trace (路径追踪) 算法难以处理的焦散现象,比如光线透过水面在水底产生的明暗条纹。

焦散:任何从光源经过反射或折射,到漫反射表面,再到眼睛(或摄影机)的光照贡献。

本项目基于VCX lab框架,通过路径追踪算法实现了直接光照渲染,通过光子映射实现了间接漫反射渲染,焦散现象的渲染,并对光子实现了可视化。此外,还尝试渲染了光束通过棱镜的散射现象。

算法亮点

  1. 实现出比较好的水面焦散效果
  2. 使用了kdTree,BVH数据结构进行加速
  3. 实现了光子贴图的可视化
  4. 尝试了色散现象

实现思路

光子映射算法分为两个阶段:1. 创建光子贴图;2. 路径追踪收集光子

阶段一:创建光子贴图

创建光子贴图时,从光源发出光子,当光子碰撞到场景中的物体并且满足指定条件时,记录该光子的位置和状态,以便阶段二中收集。

假设光源的功率为$\Phi$,光源为一个曲面$D$,面积为$A$。设$\vec x$为光源上一点,$\vec n$为该位置的法向量,$\omega$为该位置出发的一个立体角,$\Omega$为该位置的所有立体角构成的半球空间,$L_e$为该位置该立体角的辐亮度 $$ \Phi = \int_{D}d\vec x\int_{\Omega}L_e d\omega $$

1.1 光源采样

现在需要对光源发出光子进行采样,根据蒙特卡洛积分法可以得到: $$ \Phi\approx \sum_i \frac 1 N\frac {{L_e}i\cos\theta_i} {\text{pdf}{D}(\vec x)\text{pdf}_\Omega(\omega)} $$ 每一个求和项即为一个光子的初始功率。

对于点光源,辐亮度由功率向所有方向平分$L_e = \frac \Phi {4\pi}$,没有角度$\cos\theta=1$,采样为随机选择方向$\text{pdf}D(\vec x)=1, \text{pdf}\Omega(\omega)=\frac 1 {4\pi}$,则光子功率为$\Phi / N$

对于面光源,辐亮度与立体角到法向量的角度$\theta$有关,$L_e=\frac \Phi A \frac {\cos \theta} {\pi}$可是得原积分式成立。采样时使用表面随机选择位置,方向使用半球权重采样,$\text{pdf}D(\vec x)=\frac 1 A, \text{pdf}\Omega(\omega)=\frac {\cos \theta} {\pi}$,则光子功率为$\Phi / N$

photon_sample

该步骤在PhotonMapping.cpp: GeneratePhoton()中实现

1.2 光子在环境中反射

光子被发出后,将不断在环境中反射,首先使用在lab 3中实现的BVH来寻找光子与三角面的交点,并读取材质。然后使用材质的BSDF数据生成反射方向以及反射后功率。

实现时,材质由mtl文件指定,mtl中通过illum指定该三角面的类型:使用Phong模型的漫反射,理想全反射,理想折射。

如果是漫反射,则均匀地随机生成半球方向作为反射方向,再根据该反射方向,通过Bling-Phong模型计算反射的能量。由于采样的概率密度为$\frac 1 {2\pi}$,而漫反射权重$\cos \theta$在BSDF中的表示,需确保$\int_\Omega f(\omega)d\omega=1$,则$f(\omega)=\frac {\cos\theta} {\pi}$,则最终光子反射功率变化比例即为$\frac {\cos \theta} {\pi} / \frac 1 {2\pi}=2\cos\theta$

如果是全反射,则直接计算反射方向即可。

如果为折射,使用schlick估计计算折射与反射的概率,以确保在光线与反射平面接近时,能够实现全反射,且分界线不会特别明显。photon_reflect

以上部分实现在Intersecter.cpp: DirectionFromBSDF()

光子在环境中每经过一次反射,使用俄罗斯轮盘赌(即随机一个固定概率,比如0.7)继续反射,否则就直接停止。

1.3 记录光子贴图

本项目中使用了两个光子贴图:全局光子贴图,焦散光子贴图。根据光子贴图的功能,使采样的光子在环境中不断反射,达到指定条件时,记录其位置、到达方向、功率。

在阶段二路线追踪时,只有到漫反射表面才会使用光子映射,其余部分都是用路径追踪算法。所以光子仅在碰到漫反射表面时记录。

对于全局光子贴图,光子在经过一次漫反射之后(即第二次及以后)到达漫反射表面,才会记录到光子贴图中。

对于焦散光子贴图,光子在经过至少一次全反射、折射之后,碰到漫反射表面,则记录到贴图中,并且立即停止反射。

下图中,标为c (caustic)为焦散光子,g (global) 为全局光子

photon_mapping

完成记录光子后,建立kDTree数据结构,加速在阶段二中对光子的查找。

该部分代码实现在PhotonMapping.cpp: InitScene

阶段二:路径追踪渲染

渲染方程为: $$ L_r(x,\omega)=\int_\Omega f(x,\omega',\omega)L_i(x,\omega')\cos \theta' d\omega' $$ 其中$f$为BSDF函数,$L_i$为入射光线辐照度,$L_r(x,\omega)$为出射光线(指向摄像机)

render_equation

2.1 光子密度估计

由于光子携带的是功率$\Phi$不是辐亮度$L_i$,则需对方程进行一些变换 $$ L_i(x,\omega')=\frac {d^2\Phi} {dA\ \cos\theta'\ d\omega'}\ L_r(x,\omega)=\int_\Omega f(x,\omega',\omega)\frac {d^2\Phi} {dA\ \cos\theta'\ d\omega'}\cos \theta' d\omega'\ =\int_\Omega f(x,\omega',\omega)\frac {d^2\Phi} {dA\ }(\omega') $$ 则我们需要得到位置$x$所在的一小片区域,从各个方向来的功率乘以该方向的BSDF函数$f(x,\omega',\omega)$后,除以这一小片区域的面积。而从某个方向来的功率,正好是光子所记录的。

于是,可以收集该位置附近的$N$个光子,计算功率乘以BSDF的和,再除以这一片区域面积。面积采用估计的方法,找到最远的光子,到$x$的距离为$r$,使用$\pi r^2$作为面积的估计。

2.2 渲染过程

首先从相机的每个像素发射光线,通过BVH计算与场景的交点,如果交点为镜面反射或折射,则继续,直到碰到了漫反射表面时,停止反射,做进一步处理。

首先计算该位置的直接光照,从该位置向每一个光源发射光线,判断是否被物体挡住,计算出该点的亮度。(对于平面光源,则随机采样选择一个位置)

接着计算焦散光照,从焦散光子图中,找到最近的若干个光子(使用k-Dtree数据结构),通过Bling-Phong模型得到光子对视线反射的能量,再除以$\pi r^2$得到密度估计。

计算间接漫反射,从全局光子图读取,步骤同上。

该部分代码实现在每一个Case的RayTrace()函数

小优化:对于焦散光子图,在一些场景中,焦散光子只在特定的位置存在,对于焦散光子特别稀少的区域,寻找最近光子都比较远,在k-Dtree中查找速度非常慢。为此设置一个查找光子的最远距离,超过该距离的光子不再寻找,大幅提高了算法效率。

色散现象

对色散现象的视线做了一次失败的尝试

色散现象是由于不同波长的光通过玻璃的折射率不同,红色比较小,而蓝色比较大

对焦散光子图,考虑从光源分别发出三种颜色(RGB)的光子进行采样,在折射时分别使用不同的折射率进行折射

功能解释

Case栏

本项目实现了4个Case

Case 1为仅使用全局光子贴图的渲染,并且在全局光子图中包含了只有一次漫反射的光子当作直接光照。

Case 2为分离了直接光照的渲染,全局光子图中不包含只有一次漫反射的光子

Case 3为相比于前一个增加了焦散光子图,效果最好的一个Case

Case 4尝试渲染三棱镜的色散效果

选项栏

Properties mirror_caustic_photon

在左侧选项栏中,可以选择场景。

按钮Initialize进行初始化,此时下面进度条会显示生成光子的进度,生成完毕后,会在场景中将光子可视化,其中红色为全局光子图,绿色为焦散光子图。

在Case 3中,进度条将会跑两遍,第一次为全局光子图,第二次为焦散光子图。

点击Start Rendering开始渲染

下面的滑动条可以设置各项数据,分别为:

  1. 全局光子图每个光源生成光子数
  2. 焦散光子图每个光源生成光子数
  3. 焦散光子图寻找邻近光子的最大距离 (根据模型的数量级调整)
  4. 全局光子图寻找邻近光子数
  5. 焦散光子图寻找邻近光子数
  6. 采样率
  7. 路径追踪最大深度
  8. 伽马校正系数
  9. 是否启用直接光照阴影

编译即运行环境

完全使用VCX lab 3的编译即运行环境,可直接使用xmake进行编译和运行。

效果展示

以下结果均在5分钟内渲染完成

水下焦散

Sample Rate: 4 ;全局光子:10000,邻近: 200;焦散光子:6331473,邻近: 50

water_caustic_render

下两图分别为全局光子可视化图和全局光子渲染结果

water_global_photon_map water_global_render

镜面

Sample Rate: 5 ;全局光子:10000,邻近: 200;焦散光子:5737723,邻近: 50

可以观察到天花板上的光亮为镜面反射的结果

mirror_caustic_render

以下分别为光子可视化图,全局光子渲染图

mirror_caustic_photon mirror_global_render

金属球和玻璃球

Sample Rate: 4 ;全局光子:10000,邻近: 200;焦散光子:6331473,邻近: 50

可以观察到玻璃球下面光斑为玻璃球聚光效果,右侧墙上的光斑为光线经过金属球反射再进入玻璃球的结果

sphere_caustic_render

光子可视化图:

sphere_caustic_photon

三棱镜色散

rainbow

About

Final project of Introduction to Visual Computing and Interaction PKU. It implements photon mapping alogrithm which achieves caustic effect. PKU22秋可视计算与交互概论lab,以及大作业。大作业从零实现了光子映射算法,实现了比较好的焦散效果。原网址:https://gitee.com/pku-vcl/vcx

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages