GAMES101作业6

声明:使用的是vs2022版,以下内容如有问题,感谢各位大佬指正!

作业要求:

将专注于使用光线追踪来渲染图像。在光线追踪中最重要的操作之一就是找到光线与物体的交点。一旦找到光线与物体的交点,就可以执行着色并返回像素颜色。在场景中的物体数量不大时,该做法可以取得良好的结果,但当物体数量增多、模型变得更加复杂,该做法将会变得非常低效。因此,我们需要加速结构来加速求交过程。在本次练习中,我们重点关注物体划分算法 Bounding Volume Hierarchy (BVH)。本练习要求你实现 Ray-Bounding Volume 求交BVH 查找

工作框架:

└─>Renderer.cpp→生成主光线

└─>Triangle.hpp→实现Möller-Trumbore 算法

└─>Bounds3.hpp→光线与AABB相交检测

└─>BVH.cpp→BVH空间划分

作业效果:

image

💡我们需要做的:

关键词:Möller-Trumbore 算法;光线追踪;AABB;BVH;


1.在Renderer.cpp中修改Render函数:

void Renderer::Render(const Scene& scene)
{
    std::vector<Vector3f> framebuffer(scene.width * scene.height);

    float scale = tan(deg2rad(scene.fov * 0.5));
    //图像的宽高比
    float imageAspectRatio = scene.width / (float)scene.height;
    //相机位置
    Vector3f eye_pos(-1, 5, 10);
    int m = 0;
    //遍历
    for (uint32_t j = 0; j < scene.height; ++j) {
        for (uint32_t i = 0; i < scene.width; ++i) {
            // generate primary ray direction;主光线方向
            float x = (2 * (i + 0.5) / (float)scene.width - 1) *imageAspectRatio * scale;
            float y = (1 - 2 * (j + 0.5) / (float)scene.height) * scale;
            
            // TODO: Find the x and y positions of the current pixel to get the
            // direction
            //  vector that passes through it.
            // Also, don't forget to multiply both of them with the variable
            // *scale*, and x (horizontal) variable with the *imageAspectRatio*

            // Don't forget to normalize this direction!
            //归一化所有光线
            Vector3f dir = normalize(Vector3f(x, y, -1));
            //调用scene.castRay函数来追踪光线
            framebuffer[m++] = scene.castRay(Ray(eye_pos, dir), 0);//在作业5里的castRay函数放到scene里了
        }
        UpdateProgress(j / (float)scene.height);
    }
    UpdateProgress(1.f);

2.在Triangle.hpp中修改Triangle::getIntersection : 函数:

inline Intersection Triangle::getIntersection(Ray ray)
{
    Intersection inter;
    //光线方向与三角形法向量点积为正,表示光线从背面入射,不相交
    if (dotProduct(ray.direction, normal) > 0)
        return inter;
    //若det≈0,说明光线与三角形平面平行或重合,无交点。
    double u, v, t_tmp = 0;
    Vector3f pvec = crossProduct(ray.direction, e2);
    double det = dotProduct(e1, pvec);
    if (fabs(det) < EPSILON)
        return inter;

    double det_inv = 1. / det;
    Vector3f tvec = ray.origin - v0;
    //重心坐标u
    u = dotProduct(tvec, pvec) * det_inv;
    // u超出[0,1],交点在三角形外
    if (u < 0 || u > 1)
        return inter;

    Vector3f qvec = crossProduct(tvec, e1);
    //重心坐标v
    v = dotProduct(ray.direction, qvec) * det_inv;
    //v超出范围或u+v>1,交点在三角形外
    if (v < 0 || u + v > 1)
        return inter;
    
    //计算光线交点
    t_tmp = dotProduct(e2, qvec) * det_inv;
    // TODO find ray triangle intersection
    // 交点在光线反方向,无效
    if (t_tmp < 0)
        return inter;

    inter.happened = true;
    inter.coords = ray(t_tmp);  //计算交点坐标
    inter.normal = normal;      //三角形法向量
    inter.distance = t_tmp;     //交点距离
    inter.obj = this;           //相交物件指针
    inter.m = m;                //材质信息

    return inter;
}

3.在Bounds3.hpp中修改inline bool Bounds3::IntersectP 函数:

//光线与轴对齐包围盒相交检测算法(AABB)
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
                                const std::array<int, 3>& dirIsNeg) const
{
    // invDir: ray direction(x,y,z), invDir=(1.0/x,1.0/y,1.0/z), use this because Multiply is faster that Division
    // dirIsNeg: ray direction(x,y,z), dirIsNeg=[int(x>0),int(y>0),int(z>0)], use this to simplify your logic
    // TODO test if ray bound intersects
    Vector3f t1 = (pMin - ray.origin) * invDir;
    Vector3f t2 = (pMax - ray.origin) * invDir;
    Vector3f tMin = Vector3f::Min(t1, t2);                  //每组平面的最小t值(进入时间)
    Vector3f tMax = Vector3f::Max(t1, t2);                  //每组平面的最大t值(离开时间)
    float tEnter = 0.0;
    float tExit = 0.0;
    tEnter = std::max(tMin.x, std::max(tMin.y, tMin.z));    // 所有进入时间的最大值
    tExit = std::min(tMax.x, std::min(tMax.y, tMax.z));     // 所有离开时间的最小值
    //相交条件
    return tEnter < tExit && tExit >= 0;
}

4.在BVH.cpp中修改Intersection BVHAccel::getIntersection函数:

//BVH的光线追踪算法
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    // TODO Traverse the BVH to find intersection
    Intersection intersection;
    // 若节点为空或光线与当前包围盒不相交,直接返回
    if (node == nullptr || !node->bounds.IntersectP(ray, ray.direction_inv, { 0,0,0 }))
        return intersection;
    //与叶子节点中的物件(三角形)求交
    if (node->left == nullptr && node->right == nullptr)
    {
        return node->object->getIntersection(ray);          //这里调用的是obj子类:Triangle的getIntersection,即到叶子节点的包围盒开始与三角形求交
    }
    Intersection hit1 = getIntersection(node->left, ray);   //检查左子树
    Intersection hit2 = getIntersection(node->right, ray);  //检查右子树
    //返回距离最近的交点
    return  hit1.distance < hit2.distance ? hit1 : hit2;
}

【提高项】:SAH

posted @ 2025-06-30 17:05  鱼鱼莲  阅读(23)  评论(0)    收藏  举报