作业来自官网
文章目录
总览
在光线追踪中最重要的操作之一就是找到光线与物体的交点。一旦找到光线与物体的交点,就可以执行着色并返回像素颜色。在这次作业中,我们需要实现两个部分:光线的
生成和光线与三角的相交。本次代码框架的工作流程为:
- 从 main 函数开始。我们定义场景的参数,添加物体(球体或三角形)到场景
中,并设置其材质,然后将光源添加到场景中。 - 调用 Render(scene) 函数。在遍历所有像素的循环里,生成对应的光线并将
返回的颜色保存在帧缓冲区(framebuffer)中。在渲染过程结束后,帧缓冲
区中的信息将被保存为图像。 - 在生成像素对应的光线后,我们调用 CastRay 函数,该函数调用 trace 来
查询光线与场景中最近的对象的交点。 - 然后,我们在此交点执行着色。我们设置了三种不同的着色情况,并且已经
为你提供了代码。
解答
Renderer.cpp
- 先把x和y坐标放缩到[-1,1]空间内。
- 然后在把它转换到lrbt空间中,所以要乘以scale。
- 然而scale是仰角算出来的,是上下的scale,所以水平想放缩的话,还要乘以分辨率。
注意计算xy的时候的精度丢失,别忘了*1.0,要不然可能会没有图像。
void Renderer::Render(const Scene& scene)
{
std::vector<Vector3f> framebuffer(scene.width * scene.height);
//先把x和y坐标放缩到[-1,1]空间内。
//然后在把它转换到lrbt空间中,所以要乘以scale。
//然而scale是仰角算出来的,是上下的scale,所以水平想放缩的话,还要乘以分辨率。
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.0*i/scene.width-1)*scale*imageAspectRatio;
float y=(1-2.0*j/scene.height)*scale;
//t(op)=n*tan(fovY/2)
Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!
dir=normalize(dir);
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);
}
Triangle.hpp
使用Moller Trumbore算法,直接代入公式即可。
bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,
const Vector3f& dir, float& tnear, float& u, float& v)
{
Vector3f E1=v1-v0;
Vector3f E2=v2-v0;
Vector3f S=orig-v0;
Vector3f S1=crossProduct(dir,E2);
Vector3f 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&&b1>0&&b2>0&&1-(b1+b2)>0){
tnear=t;
u=b1;
v=b2;
return true;
}
return false;
}
结果
结果是build目录下的ppm格式(没见过的格式)图片。