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!

相关推荐
Cx330❀2 天前
Qt 常用控件属性与底层机制详解:从窗口半透明、浮点数陷阱到 QSS 与焦点策略
qt·ui·图形渲染
Cx330❀2 天前
Qt 常用 UI 控件详解:从QPushButton到QLabel 核心用法解析
qt·ui·图形渲染
玖玥拾6 天前
Unity Shader 基础与图形学(一)
unity·图形渲染·图形学·shader
GR9011 天前
光线追踪-2:光线照射到物体表面后,反射的Radiance怎么求
图形渲染
yyt36304584116 天前
KLineChartQuant:GLSL 的精度问题及 RTC 解法
前端·vue.js·react.js·交互·图形渲染·webgl·数据可视化
拿我格子衫来17 天前
Beam Studio:矢量图与位图如何变成 G-code
图像处理·图形渲染
maybeyaluokenai17 天前
unity build in渲染管线概述
unity·图形渲染
DolitD18 天前
实时云渲染多应用并发:CELL容器技术突破3D隔离瓶颈
云原生·容器·实时互动·图形渲染·数据可视化
hele_two19 天前
C++模拟蚁群(二)
c++·算法·数学建模·图形渲染
郝学胜-神的一滴19 天前
CMake 047:解锁安装阶段自定义操作,告别配置阶段提前执行坑
运维·服务器·c++·游戏引擎·图形渲染·opengl