game101 作业5

虚拟机代码:
作业五的任务是写一个简单的光线追踪

整体流程说明
光线传输、光线与三角形求交。我们采用了这样的方法寻找光线与场景的交点:
1,遍历场景中的所有物体,判断光线是否与它相交。在场景中的物体数量不大时,该做法可以取得良好的结果,
2,但当物体数量增多、模型变得更加复杂,该做法将会变得非常低
效。

理论依据
Whitted-Style 光线追踪
game101 作业5_第1张图片
步骤:
1,假设人眼就一个光源,所有的光线都是从人眼发出,人眼前是一个离散的二维平面,对于二维平面的每个像素点向外射出一条射线,打到三维物体上,此时会发生折射和反射

2,和光源直接连接每个折射点和散射点,观察光源是否能够到达这些点,通过深度就可以测得

3,然后每个光源可见的点,都是一条光路,此时分为首次光源和二次光源,将这些光路的能量汇总起来,就是一个像素点所得到的所有光的能量

求dir和三角形平面是否相交
依据的公式
O + tD = αA + βB + (1- α - β)C
依据上面的公式就可以推出 S S1 S2 E1 E2 的结果
game101 作业5_第2张图片

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;
}

结果:
game101 作业5_第3张图片
参考链接:
GAMES101 作业五 代码框架细节的个人理解
Whitted-Style Ray Tracing
代码参考

你可能感兴趣的:(几何学,计算机视觉,算法)