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!

相关推荐
Frank学习路上1 天前
【C++】OGRE:面向对象图形渲染库配置与示例
c++·图形渲染·ogre
Padid9 天前
Unity SRP学习笔记(二)
笔记·学习·unity·游戏引擎·图形渲染·着色器
refineiks15 天前
three.js绘制宽度大于1的线,并动态新增顶点
3d·图形渲染·webgl
Winston Wood17 天前
一个简单的例子,说明Matrix类的妙用
android·前端·图像处理·图形渲染
森龙安18 天前
VulkanTutorial(8·Shader modules)
图形渲染
云渲染图科普匠20 天前
3d室内设计效果图渲染平台哪个好?瑞云快图怎么样?
3d·图形渲染·3dsmax
先知demons21 天前
antvG6如何实现节点动画、连线动画、切换节点图标
前端·javascript·vue.js·图形渲染·canva可画
3DCAT实时渲染云21 天前
边缘计算技术的优势与挑战
实时互动·边缘计算·图形渲染
大耳猫24 天前
Android OpenGL天空盒
android·kotlin·android studio·图形渲染
大耳猫1 个月前
Android OpenGL触控反馈
android·kotlin·android studio·图形渲染