虚拟机代码:
作业五的任务是写一个简单的光线追踪
整体流程说明
光线传输、光线与三角形求交。我们采用了这样的方法寻找光线与场景的交点:
1,遍历场景中的所有物体,判断光线是否与它相交。在场景中的物体数量不大时,该做法可以取得良好的结果,
2,但当物体数量增多、模型变得更加复杂,该做法将会变得非常低
效。
理论依据
Whitted-Style 光线追踪
步骤:
1,假设人眼就一个光源,所有的光线都是从人眼发出,人眼前是一个离散的二维平面,对于二维平面的每个像素点向外射出一条射线,打到三维物体上,此时会发生折射和反射
2,和光源直接连接每个折射点和散射点,观察光源是否能够到达这些点,通过深度就可以测得
3,然后每个光源可见的点,都是一条光路,此时分为首次光源和二次光源,将这些光路的能量汇总起来,就是一个像素点所得到的所有光的能量
求dir和三角形平面是否相交
依据的公式
O + tD = αA + βB + (1- α - β)C
依据上面的公式就可以推出 S S1 S2 E1 E2 的结果
castRay函数如何理解?
1,此函数用于计算交点处的颜色;
2,如果相交对象的材质为反射和折射,我们计算反射/折射方向,并向场景中投射两条新光线;
3,当表面是透明的,我们使用菲涅尔方程的结果混合反射和折射颜色,菲涅尔方程计算反射和折射量取决于曲面法线、入射视图方向表面折射率;
4,如果表面是漫反射我们使用Phong照明模型来计算颜色
计算从像素平面发射出去的观察方向
步骤
1,因左上角为(0,0),右下角为(scene.width, scene.height),先将像素坐标系归一化到(0,1) 则有 (float)i + 0.5)/scene.width)
2,将坐标系从的范围从[0,1] 变换到 [-1,1] ,则有(2*(((float)i + 0.5)/scene.width) - 1),此时左上角的x变为 -1
3,因为像素平面不一定为正方形,则像素的形状不一定为正方形,所以以height为基准,有(2*(((float)i + 0.5)/scene.width) - 1) * imageAspectRati
4,因观察方向基于eye point 的坐标系下,所以最后乘scale,调整基于像素平面的所观察到的范围, float x = (2*(((float)i + 0.5)/scene.width) - 1) * imageAspectRatio * scale;
公式推导
代码
void Renderer::Render(const Scene& scene)
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
// 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*
float x = (2*(((float)i + 0.5)/scene.width) - 1) * imageAspectRatio * scale;
float y = (1 - 2*((float)j + 0.5)/scene.height) * scale;
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);
}
计算三角形和视线方向是否相交
步骤
1,就是套公式,具体公式推导看上面,或者看视频;
2,具体视线怎么才算和三角形相交,可以自己根据需求写, 下面的 if 条件可更改
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.
Vector3f E1 = v1-v0;
Vector3f E2 = v2-v0;
Vector3f S = orig - v0;
Vector3f S1 = crossProduct(dir, E2);
Vector3f S2 = crossProduct(S,E1);
tnear = dotProduct(S2,E2)/dotProduct(S1,E1);
u = dotProduct(S1,S)/dotProduct(S1,E1);
v = dotProduct(S2,dir)/dotProduct(S1,E1);
if(tnear>=0 && u>=0&&v>=0 &&(1-u-v)>=0) return true;
return false;
}
结果:
参考链接:
GAMES101 作业五 代码框架细节的个人理解
Whitted-Style Ray Tracing
代码参考