作业七
题目:
实现path Tracing,仅修改castRay(const Ray ray, int depth)函数,在其中实现 Path Tracing 算法
代码框架:
//
OBJ-loader模型加载库 global:全局变量 / 函数 vector:Vector3f,Vector2f类
float norm() {return std::sqrt(x * x + y * y + z * z);}/* 向量长度 */
Vector3f normalized() {/* 正规化 */
float n = std::sqrt(x * x + y * y + z * z);
return Vector3f(x / n, y / n, z / n);
}
material:材质类,包括各种材质属性参数
-
Material::Material(MaterialType t, Vector3f e)材质类型和自发光颜色m_emission
-
eval()返回fr(BRDF)
-
sample()半球均匀取样: 按照该 材质的性质,返回一个出射方向
-
PDF()返回根据 sample ()得到该出射 方向的概率密度
//
Object:物体基类 Triangle:三角形类继承Objectsphere:球体类继承Objectlight:光Arealight:区域光继承light BVH:加速类 Bounds3:包围盒类 intersection::交点信息类 ray:光线类
//
main:创建场景,材质,模型-》构建BVH-》创建渲染器,并渲染场景
Scene:场景管理类
- sampleLight()/* 根据光源的面积比例来随机选择一个光源进行采样,返回光源信息和PDF */
Renderer:光线追踪渲染器,负责发射光线,和调用castRay着色,这里修改了每个像素16次采样,
解:
根据伪代码,调用代码框架的API
Vector3f Scene::castRay(const Ray &ray, int depth) const
{
//如果没有交点,直接返回
Intersection intersec = intersect(ray);
if (!intersec.happened) {
return Vector3f();
}
//如果打到光源,获取光源自发光颜色m_emission
if (intersec.m->hasEmission()) {
return intersec.m->getEmission();
}
Vector3f l_dir(0,0,0);/* 直接光照 */
Vector3f l_indir(0,0,0);/* 间接光照 */
// 直接光照(光源通过物体直接反射到相机)
Intersection lightInter; float lightPdf = 0.0f;
sampleLight(lightInter, lightPdf);
Vector3f obj2light = lightInter.coords - intersec.coords;/* 光线 = 光源位置 - 交点位置 */
Vector3f obj2lightDir = obj2light.normalized();/* 正规化光线 */
/* ||x' - x||^2-》||obj2light||^2 -》std::sqrt(x * x + y * y + z * z) ^ 2*/
float obj2lightPow = obj2light.x * obj2light.x + obj2light.y * obj2light.y + obj2light.z * obj2light.z;/* ||x' - x||^2 */
Ray obj2lightRay(intersec.coords, obj2lightDir);/* 创建光线(起点和方向),光源到交点 */
Intersection t = intersect(obj2lightRay);/* 光线的交点 */
if (t.distance - obj2light.norm() > -EPSILON)/* 光源到交点距离 - 光源到物体的距离 > -0.00001,这在判断光线路径中,是否有遮挡 */
{
/* 光源采样渲染方程:li * fr * costheta*costheta' / ||x' - x||^2 * p{A}*/
l_dir = lightInter.emit * intersec.m->eval(ray.direction, obj2lightDir, intersec.normal)
* dotProduct(obj2lightDir, intersec.normal)
* dotProduct(-obj2lightDir, lightInter.normal)
/ obj2lightPow / lightPdf;
}
// 间接光照(光源通过 多个物体 间接反射到相机)
if (get_random_float() > RussianRoulette) {/* 俄罗斯轮盘赌注 */
return l_dir;
}
Vector3f obj2nextobjdir = intersec.m->sample(ray.direction, intersec.normal).normalized();/* 返回光线投射到的交点,一个新的采样出射方向, */
Ray obj2nextobjray(intersec.coords, obj2nextobjdir);/* 创建光线:新的采样方向 */
Intersection nextObjInter = intersect(obj2nextobjray);/* 找到采样方向的交点 */
if (nextObjInter.happened && !nextObjInter.m->hasEmission())/* 如果存在交点,且不是光源 */
{
float pdf = intersec.m->pdf(ray.direction, obj2nextobjdir, intersec.normal);/* 该出射 方向的概率密度 */
/* 渲染方程:li(递归) * fr * (n * wi) * p{wi} / P_RR(俄罗斯轮盘赌注)*/
l_indir = castRay(obj2nextobjray, depth + 1)
* intersec.m->eval(ray.direction, obj2nextobjdir, intersec.normal)
* dotProduct(obj2nextobjdir, intersec.normal)
/ pdf / RussianRoulette;
}
return l_dir + l_indir;
}
问题1:
光源是黑色的
这是因为计算反射光伪代码,仅考虑直接/ 间接光照,没有考虑从相机发射的光线是光源的情况,应返回光源的自发光颜色
问题2:
大量区域为黑色
if (t.distance - obj2light.norm() > -EPSILON)
/*光源到交点距离 - 光源到物体的距离 > -0.00001 */
考虑一下情况:
- 如果两个距离完全相等,那么差值==0,符合> -0.00001
- 光源到交点距离 < 光源到物体的距离,说明有遮挡,小的值 - 大的值,值为负的,不符合> -0.00001
可是实际上没有这么精确的情况(误差0.00001),应提高容差,否则容易导致部分地方是黑的
问题3:
有黑色条纹
其实还是精度问题
return tEnter <= tExit && tExit >= 0;
在和包围盒求交时都不要忘记 = 号
多线程:
作业必须要开启多线程,因为光线追踪实在太慢了,现在基础作业就要几分钟,之后可能要好几个小时渲一张
#include <thread>
#include <mutex>
#include <omp.h>
int spp = 16;//每个像素采样次数
std::cout << "SPP: " << spp << "\n";
int thred = 24;//使用24个线程来并行执行渲染任务
int per = scene.height/thred;
std::thread th[24];//存储24个线程的实例
renderRow()……
for(int i=0;i<thred;i++){//通过std::thread的构造函数启动每个线程,每个线程都调用renderRow函数
th[i] = std::thread(renderRow,i*per,(i+1)*per);
}
for(int i=0;i<thred;i++){//使用join()方法等待所有线程完成再执行主线程
th[i].join();
}
……
标签:光源,Vector3f,作业,intersec,交点,GAMES101,obj2light,ray
From: https://blog.csdn.net/sengyongan/article/details/142543793