Skip to content

Latest commit

 

History

History
149 lines (102 loc) · 5.05 KB

assignment5.md

File metadata and controls

149 lines (102 loc) · 5.05 KB
title date tags
GAMES101-assignment5-光线追踪
2022-03-06
GAMES101

Assignment5

光线追踪

1. 运行结果

运行结果:

img

2. 实现细节

详细代码

这次作业主要是为了实现光线追踪里的与三角形相交,主要为了实现了两个功能:

  1. 实现遍历每个像素,从每个像素发射出光线。
  2. 实现对于光线与三角形相交的判断。

2.1 遍历像素发射光线

这里要实现的目标就是遍历每一个像素,对于每一个像素计算出一个 dir 向量,来表示一个当前像素的方向向量。

具体的代码如下所示:

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 = (((i + 0.5)/scene.width)*2 - 1) *  scale * imageAspectRatio;
        float y = (1 - ((j + 0.5)/scene.height)*2) *  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);
}

这里需要注意一下坐标的空间变换(详细的描述可以见计算机图形学学习笔记——Whitted-Style Ray Tracing(GAMES101作业5讲解)_dong89801033的博客-CSDN博客),这里一共经历了四次变换,分别是:

  1. Raster space -> NDC space,把坐标归一化到(0,1)

    $$ x = (i+0.5)/width $$

    $$ y = (j+0.5)/height $$

    其中,加的0.5是为了保持在像素中心。

  2. NDC space -> Screen space, 把坐标映射到(-1,1)

    $$ x = 2*x - 1 $$

    $$ y = 1 - 2*y $$

    这里需要注意一下,y的计算是 1-2*y,原因和坐标的正负有关,因为在原本的NDC space中,y的坐标是向下 为正,然而在Screen Space里,y的坐标是向上为正,所以需要取个符号,不然图像会颠倒。

  3. 图像缩放

    因为x和y在进行归一化时,是各自被归一化到了(0,1),因此比例会变得不一样。为了保持图像比例正确,需要为横向乘上一个宽高比。

    $$ x = x * width / height $$

  4. 关于可视角度

    一般来说,摄像机(eye pos)和成像平面的距离为1,所以我们可以通过控制一个可视角度α来决定能够看到的东西的多少。例如,当α为90°时,tan(2/α) = 1, 也就是说可视角度为[-1,1]。

    所以,这里我们需要为每一个轴来乘上一个scale。

    $$ x = x*tan(\alpha/2) $$

    $$ y = x*tan(\alpha/2) $$

最后得到的坐标变化就是:

float x = (((i + 0.5)/scene.width)*2 - 1) *  scale * imageAspectRatio;
float y = (1 - ((j + 0.5)/scene.height)*2) *  scale;

2.2 光线与三角形相交

这一块的话直接套Möller-Trumbore算法的公式即可:

image-20220306133045747

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

由于外边在循环里对于每一束光线,要和每一个三角形求交,所以,这里把u,v 以及tnear传出去是为了让外边记录最小的tnear以及u,v来记录和哪一个三角形以及三角形的哪一个地方求交了。