GAMES101-Ray-Tracing框架解读(以作业7为例)(部分)
序
本博客主要用来记录自己对GAMES101
中Ray-Tracing
作业框架代码的解读(作业5-作业7所使用的代码框架),
以作业7的code
为例(当然也包括作业7的题解).
注:
场景的构建 main.cpp
这里的场景是我自己的翻译, 描述了整个渲染场景, 包括像素空间, object space 以及一些其他信息, 在这个场景中主要实现了几件事:
- 成像平面(像素平面)(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;
- 向场景中添加物体
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);
// ...
- 构造BVH(Bounding Volume Hierarchy) 注: BVH构建的代码暂时不关注
1
2 // main.cpp
scene.buildBVH();
- 配置
RayTracing
的渲染器, 并对场景进行渲染.
1
2
3 // main.cpp
Renderer r;
r.Render(scene);
产生摄像机光线(Generating Gamera Rays):Renderer.cpp
本章参考连接:
- Ray-Tracing: Generating Camera Rays
- GAMES101 作业5
理论知识:
1.
光线追踪与光栅化的区别(Ray Tracing
与
rasterization-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)
光栅化的过程:
- 将
world space
通过透视投影或者是正交投影给投影到camera space
中去. - 然后直接扔掉z轴, 判断可见性(
Clipping coordinates
) - 最后添加光照.
一定要注意这两者的区别,
Ray Tracing
不需要转换world space
,
整个过程就是在world space
中完成的;
而光栅化的过程是处理world space
,
将其投影到像素平面上去.(虽然操作的过程中可能不太一样).
2. 坐标转换(计算 camera-to-world matrix)
Raster space
->NDC space
->Screen space
整个过程类似与将整个像素平面给变换到[(-1, 1), (-1, 1)]的平面中, 对于每个像素点(取像素中点), 很容易得到以下的公式:
添加长宽比, 我们另y轴总长度为2, 计算x轴的总长度(乘以长宽比):
添加aov的影响
令AB长1, 也就是说
camera
在成像平面后单位距离, 那么长宽都各乘一个系数:相机的平移其实并不影响
camera rays
的方向, 但是如果camera
发生了旋转, 方向不是-z
的方向, 就需要进行变换.代码展示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16for (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;
}
}
}