Games101Homework【6】Acceleration structure(Including framework analysis)

Code Analysis:

friend:

C++中友元(友元函数和友元类)的用法和功能_friend class a<b>-CSDN博客

[C++:不如Coding](11):友元函数与友元类_哔哩哔哩_bilibili

Here is a simple explanation:

By using the mechanism of classes, data hiding and encapsulation are achieved. Data members of a class are usually defined as private, while member functions are generally defined as public, thus providing an interface for communication between the class and the outside world. However, sometimes it is necessary to define functions that are not part of the class but need frequent access to the class's data members. In such cases, these functions can be defined as friend functions of the class. In addition to friend functions, there are also friend classes, both of which are collectively referred to as friends. The purpose of friends is to improve the efficiency of the program (by reducing the time overhead of type checking and security checks, etc.). However, they undermine the encapsulation and hiding of the class, allowing non-member functions to access the class's private members.

Why use friends: You don't have to write a public method for a special case separately (for example, for a man and a woman, there is a marry method that is generally called only once during the lifecycle of these two classes. If you give the man or woman class a public method, marry, it is unnecessary and would make the class overly complex).

Operator overloading:

C++运算符重载_哔哩哔哩_bilibili

std::Move:

The functionality of std::move is to convert a left-value into an r-value reference, and return that r-value reference, allowing the value to be used via the r-value reference for move semantics (move constructor or move assignment operator).

std::move()与移动构造函数_std::move的作用-CSDN博客

Architecture:

Let's look at the picture first:

The logical architecture is exceptionally clear now, and I've placed the source code (with comments) in another blog.

Games101Homework【6】Code Part-CSDN博客

Principle:

Checking if a ray intersects with a plane.:

Determine the entry point and exit point:

BVH Core Algorithm (Recursive):

Code:

Render():

Transform the manually added vectors into a new Ray object:

复制代码
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*
            Ray ray(eye_pos, normalize(Vector3f(x, y, -1)));
            // Don't forget to normalize this direction!
             
            framebuffer[m++] =scene.castRay(ray,scene.maxDepth);
        }
        UpdateProgress(j / (float)scene.height);
    }
    UpdateProgress(1.f);

    // save framebuffer to file
    FILE* fp = fopen("binary.ppm", "wb");
    (void)fprintf(fp, "P6\n%d %d\n255\n", scene.width, scene.height);
    for (auto i = 0; i < scene.height * scene.width; ++i) {
        static unsigned char color[3];
        color[0] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].x));
        color[1] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].y));
        color[2] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].z));
        fwrite(color, 1, 3, fp);
    }
    fclose(fp);    
}

Triangle::getIntersection():

Calculate intersection information:

cpp 复制代码
/**
 * 获取射线与三角形的交点信息
 * 
 * @param ray 射线对象,包含射线的起点和方向
 * @return Intersection 结构体,包含交点是否发生、交点坐标、交点到射线起点的距离、
 *         对应的物体指针、交点处的法向量和材质信息
 */
inline Intersection Triangle::getIntersection(Ray ray)
{
    Intersection inter; // 初始化交点信息结构体

    // 如果射线方向与三角形法向量的点积大于0,则射线方向与三角形面向反,不产生交点
    if (dotProduct(ray.direction, normal) > 0)
        return inter;

    double u, v, t_tmp = 0;
    // 计算pvec,这是进行交点计算的一部分
    Vector3f pvec = crossProduct(ray.direction, e2);
    // 计算行列式值det,用于后续交点计算
    double det = dotProduct(e1, pvec);
    // 如果行列式的绝对值小于EPSILON,表示方向向量几乎平行,不产生交点
    if (fabs(det) < EPSILON)
        return inter;

    // 计算行列式的倒数,用于简化后续计算
    double det_inv = 1. / det;
    // 计算tvec,这是计算u参数的一部分
    Vector3f tvec = ray.origin - v0;
    // 计算参数u,如果u不在0到1之间,则交点不在三角形上
    u = dotProduct(tvec, pvec) * det_inv;
    if (u < 0 || u > 1)
        return inter;
    // 计算qvec,用于计算v参数
    Vector3f qvec = crossProduct(tvec, e1);
    // 计算参数v,如果v不在0到1之间,或u+v不在0到1之间,则交点不在三角形上
    v = dotProduct(ray.direction, qvec) * det_inv;
    if (v < 0 || u + v > 1)
        return inter;
    // 计算临时距离参数t_tmp,用于进一步判断交点是否成立
    t_tmp = dotProduct(e2, qvec) * det_inv;

    // 判断t_tmp是否小于0,若小于0,则交点在射线的起点之后,不成立
    if(t_tmp<0){
        return inter;
    }
    // 如果以上所有条件都满足,则更新交点信息为有效交点
    inter.happened = true;
    inter.coords = ray(t_tmp); // 计算交点坐标
    inter.distance = t_tmp; // 保存交点到射线起点的距离
    inter.obj = this; // 保存对应的三角形对象指针
    inter.normal = normal; // 保存交点处的法向量
    inter.m = m; // 保存材质信息

    return inter;
}

IntersectP():

"Solve equations to calculate tEnter and tExit. Based on the direction of the ray, if it is emitted in the non-positive direction, then the smaller negative t is the exit point, and the larger one is the entry point (dirIsNeg).

cpp 复制代码
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 tEnter,tExit;
    tEnter=  (pMin-ray.origin)*invDir;
    tExit =  (pMax-ray.origin)*invDir;
    if(!dirIsNeg[0]){
        std::swap(tEnter.x,tExit.x);
    }
     if(!dirIsNeg[1]){
   std::swap(tEnter.y,tExit.y);
    }
     if(!dirIsNeg[2]){
   std::swap(tEnter.z,tExit.z);
    }
   float tenter,texit;
   tenter=std::max(tEnter.x,std::max(tEnter.y,tEnter.z));
   texit=std::min(tExit.x,std::min(tExit.y,tExit.z));
   return tenter<=texit&&texit>=0;

}

getIntersection():

Implementing the core algorithm:

cpp 复制代码
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    Intersection isect;
    // TODO Traverse the BVH to find intersection
    if(!node->bounds.IntersectP(ray,ray.direction_inv,std::array<int,3>{ray.direction.x>0,ray.direction.y>0,ray.direction.z>0})) return isect;
    if((!node->left)&&(!node->right)){
        return node->object->getIntersection(ray);
    }
    auto hit1=getIntersection(node->left,ray);
    auto hit2=getIntersection(node->right,ray);
    return hit1.distance<hit2.distance?hit1:hit2;
}

Result:

cool!

相关推荐
郝学胜-神的一滴2 小时前
计算机图形学中的光照模型:从基础到现代技术
开发语言·c++·程序人生·图形渲染
玖釉-5 天前
三维模型数据结构与存储方式解析
数据结构·算法·图形渲染
玖釉-7 天前
基于屏幕空间投影面积的剔除(Screen-space Area Culling, SSAC)
图形渲染
头发掉光的程序员11 天前
第九章 纹理贴图
c++·图形渲染·direct12
jiuzhouhi11 天前
LOD and Reflections Adding Details
图形渲染
元让_vincent12 天前
论文Review SLAM R3LIVE | ICRA2022 港大MARS | 可以生成Mesh的激光视觉惯性SLAM
3d·机器人·图形渲染·slam·建图
jiuzhouhi13 天前
Directional Shadows Cascaded Shadow Maps
图形渲染
小于小于大橙子19 天前
3D Gaussian Splatting理论详解
计算机视觉·3d·图形渲染
我想_iwant19 天前
PBR 渲染基础与渲染方程
图形渲染
Vanranrr20 天前
OpenGL ES vs VG-Lite:嵌入式图形渲染引擎对比分析
ui·图形渲染