基于采蒙特卡罗路径追踪算法的光线追踪 课程报告+代码
本文介绍了一个基于蒙特卡罗路径追踪算法的光线追踪渲染系统。系统实现了景深效果(通过模拟光圈相机)、软阴影(通过面光源采样)、SSAA抗锯齿(4倍采样)以及纹理映射(支持平面和球体的UV映射)。采用AABB BVH和KD-Tree加速光线求交过程,支持GPU并行计算。系统包含多种材质类型(漫反射、镜面反射、折射)和参数曲面解析求交功能,通过多次采样实现无偏渲染,但收敛速度较慢。文中提供了代码结构说明
资源下载地址:https://download.csdn.net/download/sheziqiong/85819502
资源下载地址:https://download.csdn.net/download/sheziqiong/85819502
目录
⼀、光线追踪 1
⼆、景深、软阴影、抗锯⻮与贴图 1
- 景深 1
- 软阴影 1
- 抗锯⻮ 2
4.贴图 2
渲染效果 3 - 球⾯的颜⾊及凹凸贴图、平⾯凹凸贴图(两侧砖墙)、表⾯材质类型贴图(地⾯) 3
- 景深,三个100k⻰模型(材质分别为带颜⾊的折射、带颜⾊的反射、漫反射) 4
三、参数曲⾯解析法求交 4
渲染效果 5
四、GPU并⾏加速 5
五、光线求交加速 5
AABB BVH 5
KD-Tree 6
六、代码结构 7
⼀、光线追踪
采⽤蒙特卡罗路径追踪算法,通过多次采样从相机发出的光线并追踪其路径,计算路径上的发光、 反射、折射等带来的颜⾊权重,最后求平均以求解物体表⾯的渲染⽅程,这个过程求解的渲染⽅程结果 是⽆偏的,缺点就是收敛速度极慢,采样数直接影响了渲染结果的质量,采样数的增加⼜会导致渲染时
⻓增加。且由于只追踪从相机发出的光线(即单向路径追踪,区分于双向路径追踪),导致当光源⾯积 很⼩时很难收敛,也难以模拟焦散等特征。
实现路径追踪的基础算法时主要参考了smallpt,同时借助SmallPT —— 99 ⾏代码光线追踪解析以理解算法。实现了漫反射、镜⾯反射与折射三种表⾯材质类型。
光线追踪中⽤到的求交逻辑除了参数曲⾯外均基于前⾯⼏次PA。 代码⻅include/pathtracer.cuh
⼆、景深、软阴影、抗锯⻮与贴图
1.景深
原先的针孔相机模型中,相机(发出初始光线的位置)即为空间中⼀个点;⽽在模拟景深,即带光 圈的相机时,相机(发出初始光线的位置)可以是光圈位置的任何⼀个点,所以通过指定光圈⼤⼩,随 机化地选择初始出射光线来模拟景深,为此另外定义⼀个焦距参数。
在与相机所在位置的距离等于焦距的平⾯上,物体应该清晰可⻅,光圈中随机发出的光线需要满⾜ 这⼀特征。采⽤的⽅法是,先在光圈内随机产⽣⼀个点作为光线的出射点,然后计算这个光线的⽅向, 使得其能刚好射向焦点,出射点加⽅向即构成完整的出射光线。
景深相机的代码⻅include/camera.cuh 的getRay 部分
2.软阴影
通过对⾯光源采样,被物体阻挡的部分并不是完全⽆法采样到光照,⽽是随被遮挡的程度逐渐加深
⽽呈现出逐渐变深的阴影,因此可以得到带有渐变过渡的阴影。在实现中由于不定义光线,⽽直接给物 体材质加上发光这⼀特征,因此可以⽅便地实现⾯光源,即⾃然实现了软阴影。
不附上具体代码,实现结合在pt当中
3.抗锯⻮
参考smallpt,实现的是SSAA*4抗锯⻮,即将每个像素分为四个⼦像素进⾏采样,采样完毕后将四 个结果取平均作为对该像素本次采样的结果。SSAA的优点是实现⾮常简单,但相应的增加了数倍采样次 数,⼤⼤增加了渲染开销。
代码⻅main.cu 的renderPixel 部分,每个像素都分成四个⼦像素渲染再压缩
4.贴图
实现了平⾯与球体类的纹理映射。对于平⾯,采⽤了指定区域平铺+拉伸的映射⽅法,在创建平⾯ 时可以选择⼀个平铺的基准点以及平铺的两个⽅向向量,⽅向的⻓度同时也决定了材质的拉伸程度。对 于球体,采⽤了墨卡托投影,直接通过交点处的法向换算出uv空间坐标。
本文转载自:http://www.biyezuopin.vip/onews.asp?id=15957
其中右侧的类功能基本与前⼏次PA中相同,Material类中增加了Texture类以满⾜贴图
Texture类的主要功能是根据uv坐标返回⼀个包含所有渲染需要的材质信息的数据结 构,该接⼝定义如下
Threadresource类⽤来储存⼀些多线程的必要资源,此处可以忽略
Camera类增加了景深相关的参数
增加的参数为焦距flength 与光圈⼤⼩aperture
Camera类保存了相机位置与图⽚⼤⼩信息,其最主要的功能是产⽣从某个像素点上发 出的光线,接⼝定义如下
AABB类即为AABB包围盒,被应⽤在物体、bvhtree和kdtree上
其实现了与光线求交、与三⻆形⾯⽚求交(⽤于构建kdtree)的⽅法
KdTree作为Mesh的成员变量,⽤来加速Mesh求交
其求交逻辑主要如下,利⽤⼀个栈来实现⾮递归的树遍历,其中mark数组⽤来记录哪些
⾯⽚已经求过交,以避免重复的求交
1 device bool intersect(const Ray& r, Hit& h, float
tmin, ThreadResource* thread_resource) const {
2BoolBitField mark = thread_resource->_repeat_mark;
// pre allocated
3int mark_size = thread_resource->_mark_size;
4memset(mark, 0, sizeof(BoolBitField) * mark_size); 5
6bool result = false;
7KdNode stack[MAX_KDTREE_DEPTH * 2];
8KdNode** stackPtr = stack;
9*(stackPtr++) = nullptr; 10
11if (!mainNode->getLeftChild() && !mainNode-
getRightChild()) {
12intersect(r, h, tmin, mark, mark_size, mainNode, result);
13 }
14
15KdNode* node = mainNode;
16do {
17KdNode* lchild = node->getLeftChild();
18KdNode* rchild = node->getRightChild();
19bool lcNull = (lchild == nullptr);
20bool rcNull = (rchild == nullptr); 21
22bool intersectL = lcNull ? false : lchild-
getBox().isIntersect®;
23bool intersectR = rcNull ? false : rchild-
getBox().isIntersect®;
24
25if (intersectL && lchild->isLeaf()) {
26intersect(r, h, tmin, mark, mark_size, lchild, result);
27 }
28
29if (intersectR && rchild->isLeaf()) {
30intersect(r, h, tmin, mark, mark_size, rchild, result);
31 }
32
33bool traverseL = (intersectL && !lchild-
isLeaf());
34bool traverseR = (intersectR && !rchild-
isLeaf());
35
36if (!traverseL && !traverseR) {
37node = *(–stackPtr); //pop 38 }
39 else {
所有物体都是虚基类Object3D的派⽣类,Object3D要求物体拥有AABB包围盒与材质的成员 变量,并要求物体必须实现统⼀的求交接⼝
Objects类负责保存场景中的所有物体,控制其创建与回收,并负责在初始化场景时构建⼀棵
bvhtree并保存,⽤于后续求交。在光追算法中对场景中物体求交时,仅调⽤Objects的
bvhtree的求交接⼝






资源下载地址:https://download.csdn.net/download/sheziqiong/85819502
资源下载地址:https://download.csdn.net/download/sheziqiong/85819502
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)