Games101,作业7(模板分析)

该博客只分析较难理解的函数和用途,以及程序运行方式,简单的函数不再赘述。

  • 首先,我们将从程序运行的流程来理解代码框架。
  • 之后再按照代码文件来分析整个框架的构造思路。

程序流程(main函数进入)

构造Scene类(构造场景)

Scene scene(784, 784);

Scene

参数:
    int width = 1280;
    int height = 960;
    double fov = 40;
    Vector3f backgroundColor = Vector3f(0.235294, 0.67451, 0.843137);
    int maxDepth = 1;
    float RussianRoulette = 0.8;

	BVHAccel *bvh;
	std::vector<Object* > objects;					//模型指针集合
    std::vector<std::unique_ptr<Light> > lights;	//光源指针集合
构造函数:
    Scene(int w, int h) : width(w), height(h)
    {}

设置材质(Material类)

Material* red = new Material(DIFFUSE, Vector3f(0.0f));
red->Kd = Vector3f(0.63f, 0.065f, 0.05f);

参数:

MaterialType m_type;//只有diffuse材质(漫反射材质)
Vector3f m_emission;//辐射量
float ior;
Vector3f Kd, Ks;//Kd漫反射系数,Ks高光镜面反射系数
float specularExponent;

构造函数

Material::Material(MaterialType t, Vector3f e){
    m_type = t;//材质类型
    m_emission = e;//不知道是什么,构造时都为0.0f
}

使用:在main函数中构造了red,green,white,light 四种材质,设置了材质的漫反射系数。

加载模型(MeshTriangle类,三角形网格类)

 MeshTriangle floor("../models/cornellbox/floor.obj", white);
参数
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;							//bvh树的根指针
float area;								//表面积之和

Material* m;							//材质指针
构造函数
  1. 调用obj1::Loader载入模型(Loader类这里便不再介绍,这个类主要用于读取obj格式的文件)
objl::Loader loader;
loader.LoadFile(filename);
  1. 将模型中的所有三角形保存到三角形集合中,并且计算出整个模型的包围盒保存到bounding_box中
  2. 创建三角形指针集合ptrs,用三角形指针集合构建BVH树
std::vector<Object*> ptrs;
for (auto& tri : triangles){
      ptrs.push_back(&tri);
      area += tri.area;
}
bvh = new BVHAccel(ptrs);

对模型构建BVH树

BVHAccel 类(BVH加速器)
参数:

    BVHBuildNode* root;				//BVH树根节点。
    // BVHAccel Private Data
    const int maxPrimsInNode;       //默认值为1,在代码中没有被使用
    const SplitMethod splitMethod;	//拆分方法,默认为SplitMethod::NAIVE(原始的)
    std::vector<Object*> primitives;//三角形指针集合

构造函数:
调用recursiveBuild(递归构造)

root = recursiveBuild(primitives);

BVHBuildNode结构体

struct BVHBuildNode {
    Bounds3 bounds;			//包围盒
    BVHBuildNode *left;		//左指针
    BVHBuildNode *right;	//右指针
    Object* object;			//物体指针
    float area;				//面积
    
    ...
}
recursiveBuild函数

构建BVH树:

  1. 对于每一个结点都有该结点包含三角形的包围盒左右指针指向子结点
  2. 每次对包围盒的最长边进行分隔。
  3. 求每个三角形的重心,以重心坐标包围盒最长的方向进行排序,再二分递归建立子节点。
  4. 直到结点中只剩下1个三角形,结束递归(两个三角形建立左右结点递归)。
  5. 返回根结点

将模型添加到场景

scene.Add(&floor);

将模型指针添加到<object*>容器中。

void Add(Object *object) { objects.push_back(object); }

对场景构建BVH树

scene.buildBVH();

对object容器构建BVH树,将结果保存在Scene的bvh中。

this->bvh = new BVHAccel(objects, 1, BVHAccel::SplitMethod::NAIVE);

每一个模型看作一个整体进行树的构建。

整个BVH树的构建

先以模型为基本元素进行树的构建,再对每一个模型内部进行树的构建。

渲染

Renderer r;
r.Render(scene);

对场景中的每一个像素生成一道从视点发出的光线

framebuffer[m++] = scene.castRay(Ray(eye_pos, dir), 0);

调用castRay函数进行光线追踪。

这块内容详见 作业7,作业代码分析

存储到文件

存储为ppm文件格式。

// 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 * std::pow(clamp(0, 1, framebuffer[i].x), 0.6f));
        color[1] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].y), 0.6f));
        color[2] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].z), 0.6f));
        fwrite(color, 1, 3, fp);
    }
    fclose(fp);   

到此为止,程序便完成了最终图像的生成
下面我们对文件中各功能进行分析

对代码文件分析

代码文件中有较多并未使用的函数和许多冗杂参数,使得阅读代码时“杂音”太多。下面将只介绍光线追踪中使用到的函数。

Main.cpp

进行场景的构建,模型的添加,参数的设置,渲染的调用。

Scene类

参数
	int width = 1280;
    int height = 960;
    double fov = 40;
    Vector3f backgroundColor = Vector3f(0.235294, 0.67451, 0.843137);
    int maxDepth = 1;
    float RussianRoulette = 0.8;//俄罗斯轮盘赌
    std::vector<Object* > objects;//模型的容器(存储的是模型指针,不是三角形)
    std::vector<std::unique_ptr<Light> > lights;
    BVHAccel *bvh;
函数

向场景中添加物体

    void Add(Object *object) { objects.push_back(object); }
    void Add(std::unique_ptr<Light> light) { lights.push_back(std::move(light)); }

获取object和light的引用

    const std::vector<Object*>& get_objects() const { return objects; }
    const std::vector<std::unique_ptr<Light> >&  get_lights() const { return lights; }

光线与场景求交

Intersection intersect(const Ray& ray) const;

对object容器构造BVH树

void buildBVH();

光线追踪

Vector3f castRay(const Ray &ray, int depth) const;

光源采样,在光源中按面积均匀采样,返回pos(采样点信息)和pfd(采样点密度)

void sampleLight(Intersection &pos, float &pdf) const;

未使用的函数

    bool trace(const Ray &ray, const std::vector<Object*> &objects, float &tNear, uint32_t &index, Object **hitObject);
    std::tuple<Vector3f, Vector3f> HandleAreaLight(const AreaLight &light, const Vector3f &hitPoint, const Vector3f &N,
                                                   const Vector3f &shadowPointOrig,
                                                   const std::vector<Object *> &objects, uint32_t &index,
                                                   const Vector3f &dir, float specularExponent);
    Vector3f reflect(const Vector3f &I, const Vector3f &N) const;
    Vector3f refract(const Vector3f &I, const Vector3f &N, const float &ior) const;
    void fresnel(const Vector3f &I, const Vector3f &N, const float &ior, float &kr) const
    //由菲涅耳公式可以求出一定入射角下反射和透射的振幅、强度等

Triangle类

class Triangle : public Object

参数
    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;
    float area;
    Material* m;
函数

返回光线ray与该三角形的交点信息

Intersection Triangle::getIntersection(Ray ray)

随机得到三角形内一点,并得到该点pdf(该点的概率密度)为1/三角形面积。

void Sample(Intersection &pos, float &pdf)

得到三角形面积

float getArea();

判断该物体是否为光源

bool hasEmit();

class MeshTriangle : public Object

参数
	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;
    float area;

    Material* m;
函数

构造函数:加载模型,并将模型中的三角形加载入std::vector<Triangle> triangles中,并对该模型构造BVH树

MeshTriangle(const std::string& filename, Material *mt = new Material())

光线与该模型求交

Intersection getIntersection(Ray ray)

下面函数功能与Triangle 类相同

void Sample(Intersection &pos, float &pdf)
float getArea()
bool hasEmit()

其他未提及的函数都是没有被使用的。

BVHAccel类

struct BVHBuildNode

	Bounds3 bounds;
    BVHBuildNode *left;
    BVHBuildNode *right;
    Object* object;
    float area;
参数
    enum class SplitMethod { NAIVE, SAH };//分隔方法
    BVHBuildNode* root;
    const int maxPrimsInNode;
    const SplitMethod splitMethod;
    std::vector<Object*> primitives;
函数

构造函数
对容器p构造BVH树,返回根节点。

BVHAccel(std::vector<Object*> p, int maxPrimsInNode = 1, SplitMethod splitMethod = SplitMethod::NAIVE);

内部函数:递归构造BVH树函数(设为private更好)

BVHBuildNode* BVHAccel::recursiveBuild(std::vector<Object*> objects)

光线与root根节点下的三角形求交。

Intersection Intersect(const Ray &ray) const;

内部函数:递归搜索查找光线与node根节点下的三角形求交,返回最先打到的三角形交点。(设为private更好)

Intersection getIntersection(BVHBuildNode* node, const Ray& ray)const;

获取根节点(root)下的某个点,且返回该点密度函数(1/该根节点包含的面积)

void Sample(Intersection &pos, float &pdf);

根据p值得到某个三角形上的某一点pospdf恒为1返回。

void getSample(BVHBuildNode* node, float p, Intersection &pos, float &pdf);

其他类的重要性不大,有需要时再补充

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Elsa的迷弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值