这里回顾GAMES101 HW5,这次作业的内容是光线与三角形相交。

课程主页:

课程作业:

课程视频:

参考资料:

Render

首先要将屏幕上的像素点恢复成对应的物体坐标,而从物体坐标变成像素点即Lecture 4和Lecture5介绍的方法。

第一步,从物体坐标变成标准立方体:

第二步,从标准立方体投影到屏幕:

  • 像素的索引采用$(x, y)$的形式,其中$x$和$y$都是整数;
  • 像素的索引从$(0, 0)$到$(width - 1, height - 1)$;
  • 像素$(x, y)$以$(x + 0.5, y + 0.5)$为中心;
  • 屏幕覆盖范围$(0, 0)$到$(width, height)$;

对于将屏幕上的像素点恢复成对应的物体坐标,第一步为屏幕投影到标准矩形,对应代码为:

// [0, a) -> [-1, 1)
x = (i + 0.5 - scene.width / 2.0) / (scene.width / 2.0);
y = -(j + 0.5 - scene.height / 2.0) / (scene.height / 2.0);

注意$y$的符号是根据实际结果调整的。

第二步为从标准矩形恢复物体的坐标,假设相机距离屏幕的距离为$d$,即屏幕的$z$坐标为$-d$,根据公式:

以及下图:

可得屏幕的半宽度为:

半长度为:

即对应关系为:

因为最后只需要方向即可,所以这里取$d=1$(因为方向的原因,最后实际使用的值为$-1$),对应代码为:

// [-1, 1) -> [p, q)
x = x * scale * imageAspectRatio;
y = y * scale;
Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!
dir = normalize(dir);

rayTriangleIntersect

利用Möller Trumbore算法即可:

最后需要判断是否满足如下条件即可:

  • $t< 0$;
  • $0\le b_1 \le 1$;
  • $0\le b_2 \le 1$;
  • $0\le b_1 + b_2 \le 1$;

代码:

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.
    // u, v重心坐标
    Vector3f e1 = v1 - v0;
    Vector3f e2 = v2 - v0;
    Vector3f s = orig - v0;
    Vector3f s1 = crossProduct(dir, e2);
    Vector3f s2 = crossProduct(s, e1);

    float a = dotProduct(s1, e1);
    tnear = dotProduct(s2, e2) / a;
    u = dotProduct(s1, s) / a;
    v = dotProduct(s2, dir) / a;

    float b = u + v;

    return tnear > 0 && 0 <= u && u <= 1 && 0 <= v && v <= 1 && 0 <= b && b <= 1;
}

示例