GAMES101 作业6 详细注释版本

该代码实现了一个基于BVH(BoundingVolumeHierarchy)的数据结构来加速光线追踪算法。在构建BVH时,通过递归方式将物体按其包围盒的中心进行排序和分割,优化了求交测试的效率。此外,还包含了对三角形的交点检测函数。实验结果显示,这种方法能有效提高渲染性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作业6的文件较多,这里只主要说明几个关键代码,分别是BVH.cpp和Triangle.hpp中的部分函数,我在这里贴出这两者的代码

#include <algorithm>
#include <cassert>
#include "BVH.hpp"

BVHAccel::BVHAccel(std::vector<Object*> p, int maxPrimsInNode,
    SplitMethod splitMethod)
    : maxPrimsInNode(std::min(255, maxPrimsInNode)), splitMethod(splitMethod),
    primitives(std::move(p))
{
    time_t start, stop;
    time(&start); //记录开始构建BVH的时间
    if (primitives.empty()) //如果没有物体,直接返回
        return;

    root = recursiveBuild(primitives); //递归构建BVH

    time(&stop); //记录构建BVH结束的时间
    double diff = difftime(stop, start); //计算构建BVH所用的时间
    int hrs = (int)diff / 3600; //计算小时数
    int mins = ((int)diff / 60) - (hrs * 60); //计算分钟数
    int secs = (int)diff - (hrs * 3600) - (mins * 60); //计算秒数

    printf(
        "\rBVH Generation complete: \nTime Taken: %i hrs, %i mins, %i secs\n\n",
        hrs, mins, secs); //输出构建BVH完成,以及所用的时间
}

BVHBuildNode* BVHAccel::recursiveBuild(std::vector<Object*> objects)
{
    BVHBuildNode* node = new BVHBuildNode();

    // Compute bounds of all primitives in BVH node
    Bounds3 bounds;
    //首先是创建一个节点
    //然后遍历每个物体的getBounds方法获取包围它们的AABB的边界
    //然后通过Union函数把这些AABB合为一个大的AABB作为节点
    for (int i = 0; i < objects.size(); ++i)
        bounds = Union(bounds, objects[i]->getBounds());
    //根据当前AABB内物体的个数来决定划分的方法
    if (objects.size() == 1) {//如果只有一个物体,那么它就作为叶节点,并把它的子节点设为空
        // Create leaf _BVHBuildNode_
        node->bounds = objects[0]->getBounds();
        node->object = objects[0];
        node->left = nullptr;
        node->right = nullptr;
        return node;
    }
    else if (objects.size() == 2) {//如果是有两个物体,那么就就将这两个物体分别放入两个子节点中进行遍历,这样就保证了每个节点最多只有一个物体了
        node->left = recursiveBuild(std::vector{objects[0]});
        node->right = recursiveBuild(std::vector{objects[1]});

        node->bounds = Union(node->left->bounds, node->right->bounds);
        return node;
    }
    else {//如果大于两个物体,那么就要划分当前的BVH
        Bounds3 centroidBounds;
        for (int i = 0; i < objects.size(); ++i)//遍历物体
            centroidBounds =
                Union(centroidBounds, objects[i]->getBounds().Centroid());//通过getBounds()的Centroid()方法找到所有物体的中心,通过Union函数找到它们覆盖的范围
        int dim = centroidBounds.maxExtent();//通过maxExtent()方法判断到底是哪个轴覆盖的范围大
        //返回0就是x轴范围大,返回1就是y轴范围大,返回2就是z轴范围大
        //根据不同的情况,将物体根据中心坐标分别按照x、y、z轴排序,
        switch (dim) {
        case 0:
            std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                return f1->getBounds().Centroid().x <
                       f2->getBounds().Centroid().x;
            });
            break;
        case 1:
            std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                return f1->getBounds().Centroid().y <
                       f2->getBounds().Centroid().y;
            });
            break;
        case 2:
            std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                return f1->getBounds().Centroid().z <
                       f2->getBounds().Centroid().z;
            });
            break;
        }
        //排序完后将物体对半划分,分别放入两个子节点中进行递归,直到只剩一个物体为止
        //最后将当前节点的AABB赋值为两个子节点的Union
        auto beginning = objects.begin();
        auto middling = objects.begin() + (objects.size() / 2);
        auto ending = objects.end();

        auto leftshapes = std::vector<Object*>(beginning, middling);
        auto rightshapes = std::vector<Object*>(middling, ending);

        assert(objects.size() == (leftshapes.size() + rightshapes.size()));

        node->left = recursiveBuild(leftshapes);
        node->right = recursiveBuild(rightshapes);

        node->bounds = Union(node->left->bounds, node->right->bounds);
    }

    return node;
}

Intersection BVHAccel::Intersect(const Ray& ray) const
{
    Intersection isect;
    if (!root)
        return isect;
    isect = BVHAccel::getIntersection(root, ray);
    return isect;
}

Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    // 定义三个交点结构体
    Intersection intersect, intersectl, intersectr;
    // 检查光线是否与 BVH 节点的包围盒相交
    if (!node->bounds.IntersectP(ray, ray.direction_inv))
        return intersect;
    // 如果当前 BVH 节点是叶子节点,则计算光线与物体的交点
    if (node->left == nullptr && node->right == nullptr) {
        intersect = node->object->getIntersection(ray);
        return intersect;
    }
    // 如果当前 BVH 节点不是叶子节点,则递归地计算光线与左右子节点的交点
    intersectl = getIntersection(node->left, ray);
    intersectr = getIntersection(node->right, ray);
    // 比较两个交点的距离,返回距离较小的那个交点
    return intersectl.distance < intersectr.distance ? intersectl : intersectr;
}
#pragma once

#include "BVH.hpp"
#include "Intersection.hpp"
#include "Material.hpp"
#include "OBJ_Loader.hpp"
#include "Object.hpp"
#include "Triangle.hpp"
#include <cassert>
#include <array>

bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1,
    const Vector3f& v2, const Vector3f& orig,
    const Vector3f& dir, float& tnear, float& u, float& v)
{
    Vector3f edge1 = v1 - v0;
    Vector3f edge2 = v2 - v0;
    Vector3f pvec = crossProduct(dir, edge2);
    float det = dotProduct(edge1, pvec);
    if (det == 0 || det < 0)
        return false;

    Vector3f tvec = orig - v0;
    u = dotProduct(tvec, pvec);
    if (u < 0 || u > det)
        return false;

    Vector3f qvec = crossProduct(tvec, edge1);
    v = dotProduct(dir, qvec);
    if (v < 0 || u + v > det)
        return false;

    float invDet = 1 / det;

    tnear = dotProduct(edge2, qvec) * invDet;
    u *= invDet;
    v *= invDet;

    return true;
}
class Triangle : public Object
{
public:
    Vector3f v0, v1, v2; // vertices A, B ,C , counter-clockwise order
    Vector3f e1, e2;     // 2 edges v1-v0, v2-v0;
    Vector3f t0, t1, t2; // texture coords
    Vector3f normal;
    Material* m;

    Triangle(Vector3f _v0, Vector3f _v1, Vector3f _v2, Material* _m = nullptr)
        : v0(_v0), v1(_v1), v2(_v2), m(_m)
    {
        e1 = v1 - v0;
        e2 = v2 - v0;
        normal = normalize(crossProduct(e1, e2));
    }

    bool intersect(const Ray& ray) override;
    bool intersect(const Ray& ray, float& tnear,
                   uint32_t& index) const override;
    Intersection getIntersection(Ray ray) override;
    void getSurfaceProperties(const Vector3f& P, const Vector3f& I,
                              const uint32_t& index, const Vector2f& uv,
                              Vector3f& N, Vector2f& st) const override
    {
        N = normal;
        //        throw std::runtime_error("triangle::getSurfaceProperties not
        //        implemented.");
    }
    Vector3f evalDiffuseColor(const Vector2f&) const override;
    Bounds3 getBounds() override;
};

class MeshTriangle : public Object
{
public:
    MeshTriangle(const std::string& filename)
    {
        objl::Loader loader;
        loader.LoadFile(filename);

        assert(loader.LoadedMeshes.size() == 1);
        auto mesh = loader.LoadedMeshes[0];

        Vector3f min_vert = Vector3f{std::numeric_limits<float>::infinity(),
                                     std::numeric_limits<float>::infinity(),
                                     std::numeric_limits<float>::infinity()};
        Vector3f max_vert = Vector3f{-std::numeric_limits<float>::infinity(),
                                     -std::numeric_limits<float>::infinity(),
                                     -std::numeric_limits<float>::infinity()};
        for (int i = 0; i < mesh.Vertices.size(); i += 3) {
            std::array<Vector3f, 3> face_vertices;
            for (int j = 0; j < 3; j++) {
                auto vert = Vector3f(mesh.Vertices[i + j].Position.X,
                                     mesh.Vertices[i + j].Position.Y,
                                     mesh.Vertices[i + j].Position.Z) *
                            60.f;
                face_vertices[j] = vert;

                min_vert = Vector3f(std::min(min_vert.x, vert.x),
                                    std::min(min_vert.y, vert.y),
                                    std::min(min_vert.z, vert.z));
                max_vert = Vector3f(std::max(max_vert.x, vert.x),
                                    std::max(max_vert.y, vert.y),
                                    std::max(max_vert.z, vert.z));
            }

            auto new_mat =
                new Material(MaterialType::DIFFUSE_AND_GLOSSY,
                             Vector3f(0.5, 0.5, 0.5), Vector3f(0, 0, 0));
            new_mat->Kd = 0.6;
            new_mat->Ks = 0.0;
            new_mat->specularExponent = 0;

            triangles.emplace_back(face_vertices[0], face_vertices[1],
                                   face_vertices[2], new_mat);
        }

        bounding_box = Bounds3(min_vert, max_vert);

        std::vector<Object*> ptrs;
        for (auto& tri : triangles)
            ptrs.push_back(&tri);

        bvh = new BVHAccel(ptrs);
    }

    bool intersect(const Ray& ray) { return true; }

    bool intersect(const Ray& ray, float& tnear, uint32_t& index) const
    {
        bool intersect = false;
        for (uint32_t k = 0; k < numTriangles; ++k) {
            const Vector3f& v0 = vertices[vertexIndex[k * 3]];
            const Vector3f& v1 = vertices[vertexIndex[k * 3 + 1]];
            const Vector3f& v2 = vertices[vertexIndex[k * 3 + 2]];
            float t, u, v;
            if (rayTriangleIntersect(v0, v1, v2, ray.origin, ray.direction, t,
                                     u, v) &&
                t < tnear) {
                tnear = t;
                index = k;
                intersect |= true;
            }
        }

        return intersect;
    }

    Bounds3 getBounds() { return bounding_box; }

    void getSurfaceProperties(const Vector3f& P, const Vector3f& I,
                              const uint32_t& index, const Vector2f& uv,
                              Vector3f& N, Vector2f& st) const
    {
        const Vector3f& v0 = vertices[vertexIndex[index * 3]];
        const Vector3f& v1 = vertices[vertexIndex[index * 3 + 1]];
        const Vector3f& v2 = vertices[vertexIndex[index * 3 + 2]];
        Vector3f e0 = normalize(v1 - v0);
        Vector3f e1 = normalize(v2 - v1);
        N = normalize(crossProduct(e0, e1));
        const Vector2f& st0 = stCoordinates[vertexIndex[index * 3]];
        const Vector2f& st1 = stCoordinates[vertexIndex[index * 3 + 1]];
        const Vector2f& st2 = stCoordinates[vertexIndex[index * 3 + 2]];
        st = st0 * (1 - uv.x - uv.y) + st1 * uv.x + st2 * uv.y;
    }

    Vector3f evalDiffuseColor(const Vector2f& st) const
    {
        float scale = 5;
        float pattern =
            (fmodf(st.x * scale, 1) > 0.5) ^ (fmodf(st.y * scale, 1) > 0.5);
        return lerp(Vector3f(0.815, 0.235, 0.031),
                    Vector3f(0.937, 0.937, 0.231), pattern);
    }

    Intersection getIntersection(Ray ray)
    {
        Intersection intersec;

        if (bvh) {
            intersec = bvh->Intersect(ray);
        }

        return intersec;
    }

    Bounds3 bounding_box;
    std::unique_ptr<Vector3f[]> vertices;
    uint32_t numTriangles;
    std::unique_ptr<uint32_t[]> vertexIndex;
    std::unique_ptr<Vector2f[]> stCoordinates;

    std::vector<Triangle> triangles;

    BVHAccel* bvh;

    Material* m;
};

inline bool Triangle::intersect(const Ray& ray) { return true; }
inline bool Triangle::intersect(const Ray& ray, float& tnear,
                                uint32_t& index) const
{
    return false;
}

inline Bounds3 Triangle::getBounds() { return Union(Bounds3(v0, v1), v2); }

inline Intersection Triangle::getIntersection(Ray ray)
{
    Intersection inter;

    // 检查光线方向与三角形法向量的点积是否大于零
    if (dotProduct(ray.direction, normal) > 0)
        return inter;
    //用 Möller–Trumbore 算法计算光线与三角形的交点
    double u, v, t_tmp = 0;
    Vector3f pvec = crossProduct(ray.direction, e2);
    double det = dotProduct(e1, pvec);
    // 如果 det 的绝对值小于 EPSILON,则返回空的交点结构体
    if (fabs(det) < EPSILON)
        return inter;

    double det_inv = 1. / det;
    Vector3f tvec = ray.origin - v0;
    u = dotProduct(tvec, pvec) * det_inv;
    // 如果 u 小于零或大于一,则返回空的交点结构体
    if (u < 0 || u > 1)
        return inter;
    Vector3f qvec = crossProduct(tvec, e1);
    v = dotProduct(ray.direction, qvec) * det_inv;
    // 如果 v 小于零或者 u + v 大于一,则返回空的交点结构体
    if (v < 0 || u + v > 1)
        return inter;
    // t_tmp表示光线与三角形相交时光线上的距离
    t_tmp = dotProduct(e2, qvec) * det_inv;
    // 计算并更新交点结构体
    inter.happened = true;
    inter.obj = this;
    inter.distance = t_tmp;
    inter.normal = normal;
    inter.coords = ray(t_tmp);
    inter.m = this->m;

    return inter;
}

inline Vector3f Triangle::evalDiffuseColor(const Vector2f&) const
{
    return Vector3f(0.5, 0.5, 0.5);
}

凭借一些个人的理解对大多数代码加上了注释,有些地方肯定会有理解不到位或者不对的情况,欢迎大佬留言指正。

完成内容:完成了Whittd-style的光线追踪渲染,使用BVH结构进行加速求交

实验结果:

  完整代码git自取:Yuchenpoet/CG (github.com)   

### Games101 作业6 解决方案与资料 #### 完整的环境配置指南 对于 Mac M1 用户,在准备完成 Games101 的第六次作业前,建议先按照官方提供的《GAMES101 作业环境配置指北》来设置开发环境[^1]。这份文档不仅涵盖了如何安装必要的包管理工具以及依赖库,还提供了详细的编译指导——无论是通过命令行直接操作还是借助 Makefile 或者更复杂的 CMake 工具。 #### 关于光线追踪优化技巧 针对可能遇到的画面质量问题,比如由于减少反射次数而导致的噪点增加现象,可以通过调整采样策略加以改善。尽管降低最大递归深度确实有助于缓解性能瓶颈并防止光线路径过长引起的计算溢出,但这通常意味着每像素的有效样本数减少了,从而影响最终图像的质量[^2]。因此,考虑引入更加高效的随机化算法或是采用分层抽样的方法可能是更好的选择。 #### Lambda 表达式的应用实例 当涉及到复杂图形编程时,lambda 函数因其简洁性和灵活性而备受青睐。特别是在处理回调机制或需要临时定义一些短小精悍的操作逻辑场景下尤为有用。例如,在实现某种特定效果的过程中,如果希望某个局部作用域内的匿名函数可以直接访问外部上下文中声明过的变量,则可以利用 `&` 符号指定捕获列表,使得该 lambda 可以按引用方式获取这些资源,并对其进行修改[^3]。 #### 黑边问题及其修复措施 在渲染过程中偶尔会出现物体边缘出现不必要的黑色线条的情况,这通常是由于几何体之间的间隙或者是纹理映射不连续所造成的视觉伪影。对此类问题的一个常见修正手段是在着色器代码里加入额外判断条件,确保相邻表面之间平滑过渡;另外也可以尝试微调相机视角参数或者重新审视模型文件本身是否存在缺陷[^4]。 ```cpp // 示例:C++ 中使用 Lambda 捕获父级作用域中的变量 void example() { int value = 42; auto func = [&]() { std::cout << "Captured Value: " << ++value << '\n'; }; func(); // 输出 Captured Value: 43 并更新原值 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值