首页 > 其他分享 >GAMES101(作业7)

GAMES101(作业7)

时间:2024-09-26 23:23:42浏览次数:10  
标签:光源 Vector3f 作业 intersec 交点 GAMES101 obj2light ray

 作业七

题目:

实现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

相关文章