一开始被“Ambient Occlusion”这个名字唬到了,然后又是什么“cosine distribution”和“samples on hemisphere”,感觉神乎其神。
后来,通过程序生成了相关图形之后,才恍然觉得和之前在“问题十九:怎么模拟ray tracing中漫射球体的颜色(diffuse materials)”基本是同一回事。
http://blog.csdn.net/libing_zeng/article/details/54428306
只是,之前还没有“环境光”的概念,根本没有意识到只考虑了环境光。
当时对撞击点的着色大概是这样:
在撞击点处作以单位法向量,然后以单位法向量的另一端为球心作一个单位球面;
然后在球面上随机取一点来确定入射光线的方向;
然后判断该“入射光线”是否撞击到其他物体,每撞击到一次,光线强度进行相应的衰减。
我们接下来正儿八经学习的“环境光遮蔽”,也基本是这个方法:
光线撞击物体,得到撞击点;
然后,对撞击点处的单位上半球面进行采样得到很多个采样点;
然后,撞击点分别和这“很多个采样点”组合得到“很多条阴影光线”;
若阴影光线撞击到其他物体,说明这条阴影光线对应的环境光线被其他物体挡住了,若阴影光线没有撞击到其他物体,说明这条阴影光线对应的环境光线能够顺利到达撞击点;
然后,根据顺利到达撞击点的阴影光线的条数来给撞击点进行着色。
之前,我们认为来自各个方向的环境光的强度是一样的,不考虑环境光可能被其他物体遮挡住。对应生成的图形,则是扁平的。例如:
但是实际上,只要环境中的物体个数大于一个,物体接受的环境光就会被其他物体遮挡。如下图示意:
有一种比较实际的做法是:
对撞击点处的单位上半球面进行采样得到很多个采样点;
然后,撞击点分别和这“很多个采样点”连接得到“很多条阴影光线”;
若阴影光线撞击到其他物体,说明这条阴影光线对应的环境光线被其他物体挡住了,若阴影光线没有撞击到其他物体,说明这条阴影光线对应的环境光线能够顺利到达撞击点;
然后,根据顺利到达撞击点的阴影光线的条数来给撞击点进行着色(撞击点p处的光线强度正比于“顺利到达撞击点的阴影光线的条数”)。
根据反射函数:
由于这个原因,我们以撞击点p为球心作一个单位上半球:
实现的方式可能很多,书上的做法是:定义一个叫做“AmbientOccluder”的新类来实现环境光遮蔽的相关操作,这个类是Light的子类,接下来将会顶替原先Ambient类的角色。
相关代码截图如下:
定义AmbientOccluder:下截图主要示意和之前Ambient不同的地方。
AmbientOccluder的具体实现相关的方法:
Phong材质的着色函数中:
Pinhole(针孔相机)的渲染函数中:
World::build()初始化函数中:
这张开篇贴出的图形:(ka=0.5,不考虑环境光遮挡,无光源,单像素采样次数为1)
(ka=0.5,考虑环境光遮挡,无光源,单像素采样次数为1)
(就是这个鬼样子,因为使用上半球的一个随机采样点对应的光线强度作为了所有采样点的平均值,误差太大。其中的黑点:说明对应的阴影光线撞击到了其他物体,对应的原撞击点则处在阴影中,即为黑点。若将下方的绿色平面撤掉使得环境中只包含单个球面,此时就没有“环境光遮蔽”一说,因为“没有其他物体来挡住你的环境光”。如下图:)
(ka=0.5,考虑环境光遮挡,无光源,单像素采样次数为100)
(将单像素的采样次数提高到100,这样一来相当于使用了上半球上100个采样点,减小了误差)
(ka=0.5,考虑环境光遮挡,光源kd=0.5/ks=0.25/exp=20,单像素采样次数为100)
(ka=0.1,考虑环境光遮挡,光源kd=0.5/ks=0.25/exp=20,单像素采样次数为100,分别率200*200)
(ka=0.1,考虑环境光遮挡,光源kd=0.5/ks=0.25/exp=20,单像素采样次数为100,分别率400*400)
这个是“问题六十九:阴影(Shadow)”章节最后生成的图形:
(ka=0.1,不考虑环境光遮挡,光源kd=0.5/ks=0.25/exp=20,单像素采样次数为100)
然后,考虑环境光遮挡后的图形:
(ka=0.1,考虑环境光遮挡,光源kd=0.5/ks=0.25/exp=20,单像素采样次数为100)
(ka=0.5,考虑环境光遮挡,光源kd=0.5/ks=0.25/exp=20,单像素采样次数为100)
完整的代码,参考:http://download.csdn.net/detail/libing_zeng/9764789
Referrance
[1]. Kevin Suffern, Ray Tracing from theGround Up, A K Peters Ltd, 2007.