目录
编译时的坑:
因为我是在 windows 环境下用 vs2019 做作业, 所以遇到了编译上的问题, CMakeLists 文件中的这一句 target_compile_options(RayTracing PUBLIC -Wall -Wextra -pedantic -Wshadow -Wreturn-type -fsanitize=undefined), 需要改为 target_compile_options(RayTracing PUBLIC -Wall -pedantic -fsanitize=undefined), 不过不知道最好的解决方案是不是这种。因为删去的这几个参数(或是将这几个参数的 'W' 删除)是针对 GCC 编译的, 如果不删去, 那么在 vs 上编译就会报错。原因在stackoverflow上有解答。
cmake_minimum_required(VERSION 3.10)
project(RayTracing)
set(CMAKE_CXX_STANDARD 17)
add_executable(RayTracing main.cpp Object.hpp Vector.hpp Sphere.hpp global.hpp Triangle.hpp Scene.cpp Scene.hpp Light.hpp Renderer.cpp)
target_compile_options(RayTracing PUBLIC -Wall -Wextra -pedantic -Wshadow -Wreturn-type -fsanitize=undefined)
target_compile_features(RayTracing PUBLIC cxx_std_17)
target_link_libraries(RayTracing PUBLIC -fsanitize=undefined)
第一题(重心坐标求交)
题目: 计算光线和三角形是否有交点
bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,
const Vector3f& dir, float& tnear, float& u, float& v)
{
// TODO: Implement this function that tests whether the triangle
// that's specified bt v0, v1 and v2 intersects with the ray (whose
// origin is *orig* and direction is *dir*)
// Also don't forget to update tnear, u and v.
return false;
}
解析:
1. 利用重心坐标简化求交计算:
移项之后可以更为清晰的对照上述公式:
对照上述公式换元后得
S1, S2 是为了辅助计算克拉默法则时需要的行列式(用标量三重积计算出行列式):
此处只写两个, 其余的两个式子使用相同方法即可算出,接下来要做的就是对着公式敲代码了!
2. 代码:
bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,
const Vector3f& dir, float& tnear, float& u, float& v)
{
/* TODO: Implement this function that tests whether the triangle
that's specified by v0, v1 and v2 intersects with the ray (whose
origin is *orig* and direction is *dir*)
Also don't forget to update tnear, u and v.*/
Vector3f e1 = v1 - v0, e2 = v2 - v0;
Vector3f s = orig - v0, s1 = crossProduct(dir, e2), s2 = crossProduct(s, e1);
float t = dotProduct(s2, e2) / dotProduct(s1, e1);
float b1 = dotProduct(s1, s) / dotProduct(s1, e1);
float b2 = dotProduct(s2, dir) / dotProduct(s1, e1);
if (t >= 0.0 && b1 >= 0.0 && b2 >= 0.0 && (1 - b1 - b2) >= 0.0)
{
tnear = t;
u = b1;
v = b2;
return true;
}
return false;
}
第二题
题目: 计算出屏幕的像素在投影视图上的坐标
void Renderer::Render(const Scene& scene)
{
std::vector<Vector3f> framebuffer(scene.width * scene.height);
float scale = std::tan(deg2rad(scene.fov * 0.5f));
float imageAspectRatio = scene.width / (float)scene.height;
// Use this variable as the eye position to start your rays.
Vector3f eye_pos(0);
int m = 0;
for (int j = 0; j < scene.height; ++j)
{
for (int i = 0; i < scene.width; ++i)
{
// generate primary ray direction
float x;
float y;
// 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*
Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!
dir = dir / sqrt(x * x + y * y + 1); //有没有进行归一化对结果好像没有影响
framebuffer[m++] = castRay(eye_pos, dir, scene, 0);
}
UpdateProgress(j / (float)scene.height);
}
// 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] = (char)(255 * clamp(0, 1, framebuffer[i].x));
color[1] = (char)(255 * clamp(0, 1, framebuffer[i].y));
color[2] = (char)(255 * clamp(0, 1, framebuffer[i].z));
fwrite(color, 1, 3, fp);
}
fclose(fp);
}
解析: 直接上代码
void Renderer::Render(const Scene& scene)
{
std::vector<Vector3f> framebuffer(scene.width * scene.height);
float scale = std::tan(deg2rad(scene.fov * 0.5f));
float imageAspectRatio = scene.width / (float)scene.height;
// Use this variable as the eye position to start your rays.
Vector3f eye_pos(0);
int m = 0;
for (int j = 0; j < scene.height; ++j)
{
for (int i = 0; i < scene.width; ++i)
{
// generate primary ray direction
float x = (2 * (i + 0.5f) / (float)(scene.width - 1) - 1) * imageAspectRatio * scale;
float y = (1 - 2 * (j + 0.5f) / (float)(scene.height - 1)) * 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*
Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!
framebuffer[m++] = castRay(eye_pos, dir, scene, 0);
}
UpdateProgress(j / (float)scene.height);
}
// 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] = (char)(255 * clamp(0, 1, framebuffer[i].x));
color[1] = (char)(255 * clamp(0, 1, framebuffer[i].y));
color[2] = (char)(255 * clamp(0, 1, framebuffer[i].z));
fwrite(color, 1, 3, fp);
}
fclose(fp);
}