GAMES101-Ray-Tracing框架解读(以作业7为例)(部分)

本博客主要用来记录自己对GAMES101Ray-Tracing作业框架代码的解读(作业5-作业7所使用的代码框架), 以作业7的code为例(当然也包括作业7的题解).

注:

  1. 操作系统: Ubuntu20.04-WSL
  2. 学习资料:
  3. 解读逻辑: 按照code执行顺序解读, 其中遇到部分内容时可能会加上课上或者补充资料中讲过的知识.

场景的构建 main.cpp

这里的场景是我自己的翻译, 描述了整个渲染场景, 包括像素空间, object space 以及一些其他信息, 在这个场景中主要实现了几件事:

  1. 成像平面(像素平面)(Raster Space)的指定, 成像平面的大小, 以及fov(默认fov是40)
1
2
3
4
5
6
7
8
// main.cpp
Scene scene(784, 784);

// Scene.hpp
// 构造函数
Scene(int w, int h) : width(w), height(h){}
// 成员变量: 这三个成员变量可以指定像素平面
int width; int height; double fov;
  1. 向场景中添加物体object以及光源light, 并添加材质(Material), object space -> world space
1
2
3
4
5
6
7
8
9
10
// main.cpp
Material* red = new Material(DIFFUSE, Vector3f(0.0f));
red->Kd = Vector3f(0.63f, 0.065f, 0.05f);
// ...

MeshTriangle left("../models/cornellbox/left.obj", red);
// ...

scene.Add(&left);
// ...
  1. 构造BVH(Bounding Volume Hierarchy) 注: BVH构建的代码暂时不关注
1
2
// main.cpp
scene.buildBVH();
  1. 配置RayTracing的渲染器, 并对场景进行渲染.
1
2
3
// main.cpp
Renderer r;
r.Render(scene);

产生摄像机光线(Generating Gamera Rays):Renderer.cpp

本章参考连接:

  1. Ray-Tracing: Generating Camera Rays
  2. GAMES101 作业5

理论知识:

1. 光线追踪与光栅化的区别(Ray Tracingrasterization-based renderers的区别)

具体的区别请详细阅读参考连接1的第三部分, 详细介绍了这两个方式的区别. 也有很多专业术语, 这里我简单介绍一下:

Ray Tracing

Ray Tracing的过程是从人眼(也就是Camera)出发, 向每个像素发射光线(光路的可逆性, 其实是从这个光路接受的光线). 这个光线被称为Camera Ray 或者 primary ray

Ray Tracing的过程中, 我们只需要获得Camera Ray光线, 假设我们已知eye_point, 那么只需要知道光线的方向即可. 详细的流程后面描述, 如果我们有了标准情况下的光线的方向, 也即摄像机朝向-z方向, 摄像机位置为(0, 0, -1), 成像平面垂直z轴, 大小满足fov以及AspectRatio, 为了获得光线的方向, 只需要这样的一个变换, 也即 Camera-to-world matrix

同时, 最重要的是, 整个world space是不需要转换的(对比光栅化的过程).

光栅化(rasterization-base renderers)

光栅化的过程:

  1. world space通过透视投影或者是正交投影给投影到camera space中去.
  2. 然后直接扔掉z轴, 判断可见性(Clipping coordinates)
  3. 最后添加光照.

一定要注意这两者的区别, Ray Tracing不需要转换world space, 整个过程就是在world space中完成的; 而光栅化的过程是处理world space, 将其投影到像素平面上去.(虽然操作的过程中可能不太一样).

2. 坐标转换(计算 camera-to-world matrix)

  1. Raster space -> NDC space -> Screen space

    整个过程类似与将整个像素平面给变换到[(-1, 1), (-1, 1)]的平面中, 对于每个像素点(取像素中点), 很容易得到以下的公式:

  2. 添加长宽比, 我们另y轴总长度为2, 计算x轴的总长度(乘以长宽比):

  3. 添加aov的影响

    令AB长1, 也就是说camera在成像平面后单位距离, 那么长宽都各乘一个系数:

  4. 相机的平移其实并不影响camera rays的方向, 但是如果camera发生了旋转, 方向不是-z的方向, 就需要进行变换.

    代码展示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;

    // 这里默认 camera 朝向 -z 方向.
    // 因此, 不需要进行 camera-to-world 的转换
    // 在scrathapixel中给的代码中有 camera-to-world matrix这一步
    Vector3f dir = normalize(Vector3f(-x, y, 1));
    for (int k = 0; k < spp; k++){
    framebuffer[m] += scene.castRay(Ray(eye_pos, dir), 0) / spp;
    }
    }
    }