smallpt: Global Illumination in 99 lines of C++
smallpt: Global Illumination in 99 lines of C++
光线追踪
正向光线追踪
正向光线追踪符合常识:光线从发光物体出发出,“撞击”到被观察物体上,经过一系列光线传输进入人眼。
因此正向光线追踪的基本流程可以简述为:
- 追踪从发光体发射的所有光线
- 检测光线是否“撞击”物体
- 如果没有“撞击”物体,直接抛弃
- 如果撞击到物体。看反射光线是否直接射入人眼
-如果没有直接射入人眼,则其可能经过一系列传输才射入人眼(间接射入人眼),需要进步判断
-如果直接射入人眼,则计算当前反射光线的颜色,作为该撞击点的颜色
从上述流程不难可以看出,正向光线追踪需要追所有光线。而在这所有光线中,只有一部分光线会“撞击”到观察体,“撞击”到观察体的光线也只有部分会射入人眼。因此追踪所有光线,计算量大且做无用功较多。
逆向光线追踪介绍
追踪光线的方向与正向相反:从眼睛处射出光线,追踪光线射击到物体后,是否能回到光源。如果能则说明该点被光源照亮,否则物体上该点可能被间接照亮,需要进一步判断。
逆向光线追踪为什么比正向光线追踪号好呢?
因为从图形学的角度来看
人眼发射出的光线是有限的,而光源发射出的光线是无限的:人眼接收图像是二维的像素组成的。每个像素记录着该点的颜色。也即对于每个像素计算其颜色, 代表着该像素上一次光线追踪的结果。
因此逆向光线追踪的基本流程可以简述为:
FOR 每个像素点 :
构造人眼入射光线Ray
光线追踪Ray :
计算与光线Ray相交的最近的物体Obj
IF obj == null :
该像素点颜色为缺省值:全局环境光AmbientColor
Continue
ELSE
看反射光Reflection是否能直接与光源相连(未被其他物体遮挡)
IF 没有被遮挡
该像素点颜色为光源颜色在该材质上的作用
ELSE IF 被遮挡
该像素点颜色根据反射光的光线追踪结果得到。
蒙特卡罗光线追踪算法
个人理解,不对欢迎指正
蒙特卡洛思想介绍
蒙特卡罗光线追踪对逆向光线追踪模型进行改进,其中最大的区别在于把概率模型引入光线追踪。
- 逆向光线追踪中物体的表面材质很单一。引入俄罗斯赌盘轮,可以设定漫反射、镜面反射、甚至折射的概率。丰富表面材质的显示
- 每个像素点只采样一条光线计算出的颜色,正确率不高。引入蒙特卡罗可以多次采样求平均,优化渲染结果。
根据上述分析光线追踪算法中最重要的步骤可以分解成两个:
- 射线与多边形物体的求交判断(空间划分kdTree)
- 光线追踪这个递归子算法(包括对各种材质的处理)
下面由于准备材料不够,我先只主要介绍方面2:
非透明材质
漫反射材质
漫反射材质表现为表面不规则,因此反射光线的方向无法确定判断。它朝可能的任意方向反射。
我们假定对于漫反射材质,反射光线的方向范围可以限定在以撞击点为圆心,撞击点法相为中心的半圆内。
假设:
入射光线为Ray(x0, d0)。其中x0表示光线起点 d代表光线方向
入射光线与物体表面的相交点为x
入射光线与物体表面相交点x出的法向为n
目标:求反射光线Reflection Ray(x, d1).
1.构建以x点为中心,n为一个坐标轴的笛卡尔直角坐标系
w = n
u=((fabs(w.x)>.1?Vec(0,1,0):Vec(1,0,0)) * w).norm()
v=w * u
u/v/w即组成一个笛卡尔直角坐标系
2.将反射光线方向d1分解为u/v/w表示形式
如下图所示
d1 = |d1| * cosα * cosθ * u + |d1| * sinα * w + |d1| * cosα * sinθ * v
其中α为 [0, PI/2]中的随机数;θ为[0,2*PI]中的随机数
这两个步骤对应smallpt: Global Illumination in 99 lines of C++中代码片段56-60:
if (obj.refl == DIFF){
// Ideal DIFFUSE reflection
double<