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!

相关推荐
摩尔线程1 天前
使用MTVerseXR SDK实现VR串流
3d·xr·图形渲染·vr·摩尔线程
成都渲染101云渲染66666 天前
渲染太慢?Maya云渲染教程
云计算·图形渲染
Bartender_Jill13 天前
[PICO VR]Unity如何往PICO VR眼镜里写持久化数据txt/json文本
unity·json·游戏引擎·图形渲染·vr
杳戢13 天前
技术美术百人计划 | 《4.1 Bloom算法》笔记
人工智能·笔记·深度学习·计算机视觉·unity·图形渲染·技术美术
gameckisme24 天前
Selfloss,官方中文,解压即玩,
游戏·unity·游戏程序·图形渲染·cocos2d·贴图·游戏策划
refineiks24 天前
three.js使用3DTilesRendererJS加载3d tiles数据
前端·3d·图形渲染·webgl
Padid25 天前
文章-深入GPU硬件架构及运行机制 学习后记
笔记·学习·硬件架构·图形渲染·着色器
Padid1 个月前
OpenGL GLFW OIT 实现
c++·笔记·学习·图形渲染·着色器
玖er悠1 个月前
Unity Shader实现简单的各向异性渲染(采用各向异性形式的GGX分布)
unity·图形渲染
米芝鱼1 个月前
UnityShader自定义属性特性
开发语言·游戏·unity·游戏引擎·图形渲染·着色器