Games101作业5解析

1. 作业要求

  Renderer.cpp 中的 Render():这里你需要为每个像素生成一条对应的光线,然后调用函数 castRay() 来得到颜色,最后将颜色存储在帧缓冲区的相 应像素中。

  Triangle.hpp 中的 rayTriangleIntersect(): v0, v1, v2 是三角形的三个 顶点,orig 是光线的起点,dir 是光线单位化的方向向量。tnear, u, v 是你需 要使用我们课上推导的 Moller-Trumbore 算法来更新的参数。

2. 原理分析

  在RayTracing的过程当中,我们对image plane上的每一个像素进行光线投射,需要注意的是:

  1. 投射对应的平面是光栅化空间,也就是屏幕空间,由一个个像素点构成的,在计算光线的时候我们需要对其坐标进行转换
    1. 也就是屏幕空间 ——》 NDC标准设备 ——》 相机空间
    2. 可以理解为光栅化的逆过程
    3. 为什么要进行转换?
      1. 空间中其他物体坐标是基于世界空间坐标系的,光线求交的过程需要转化成相同的坐标系
      2. 光线的射线方程: O + tD
  2. 我们是对像素中心点进行投射的,坐标为 : ( i +0.5 , j+0.5 )
  3. 将坐标转化成  [0,1] 的空间上,则变成:( ( i+0.5 )/width , ( j+0.5) /height )
  4. 将 [0,1] 的平面映射回 [-1,1] 的空间当中
    1. x 变成:  2 * ( i+0.5 )/width - 1
    2. y :变成: 2 * ( j+0.5) /height - 1 
      1. 这里需要注意的是 y 的变化,当(0,0)点不在左下角时,y求值需要进行翻转(作业5中(0,0)点在左上角)
      2. y :变成: 1 - 2 * ( j+0.5) /height
  5. 转换之后我们发现一个问题:原来屏幕空间是 [width,height]的,但是现在变成了 [-1,1]
    1. 这不对呀!比例怎么消失了呢?我们发现像素被压缩了,已经不是正方形的像素了,因此我们需要进行转换
    2. x 变成:  (2 * ( i+0.5 )/width - 1 ) * AspectRatio
      1. AspectRatio为屏幕空间的宽高比
  6. 相机发射的时候是有一个视野角的,fov
    1.   
    2. 默认情况下,投射射线时,成像平面和相机 z 轴方向距离为1,相机为针孔相机模型,面向 -z 轴,位置在原点 (0,0,0)
    3. 因此 α = 90° ,注意 y 变换到 [-1,1]了
    4. 那么 || AB || = 1 = tan (α /2)
  7. 我们需要将NDC标准设备下坐标转化成相机空间下的坐标:
    1. x 变成 : ( 2 * ( i+0.5 )/width - 1 ) * AspectRatio * tan( α / 2)
    2. y :变成: ( 1 - 2 *( j+0.5) /height ) * tan( α / 2)
  8. 相机原点在(0,0,0),因此 O = (0,0,0), D = ( ( 2 * ( i+0.5 )/width - 1 ) * AspectRatio * tan( α / 2 ),( 1 - 2 *( j+0.5) /height ) * tan( α / 2 ) ,-1)
  9. 射线 O + tD,我们利用MT算法求解射线和物体的最近三角形表面交点进行着色即可

3. Render实现 

for (int j = 0; j < scene.height; ++j)
{
    for (int i = 0; i < scene.width; ++i)
    {
        // generate primary ray direction
        float x;
        float y;
        //映射到 [-1,1]
        x = 2 * (i + 0.5) / scene.width - 1;
        //y = 2 * (j + 0.5) / scene.height - 1;
        //作业里定义的坐标和opengl有点不同,原点在左上角
        y = 1 - 2 * (j + 0.5) / scene.height;
        //转换为相机空间
        x = x * scale * imageAspectRatio;
        y = y * scale;

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

 这里需要注意的是:y 的 计算是相反的,然后最后需要对dir进行标准化

4. rayTriangleIntersect相交实现

//MT算法实现:
//会出现小蓝点,浮点数的运算问题
Vector3f e1 = v1 - v0;
Vector3f e2 = v2 - v0;
Vector3f s = orig - v0;
Vector3f s1 = crossProduct(dir, e2);
Vector3f s2 = crossProduct(s, e1);
float m = dotProduct(s1, e1);
//计算参数
tnear = dotProduct(s2, e2) / m;
u = dotProduct(s1, s) / m;
v = dotProduct(s2, dir) / m;
  
//这里需要注意的是:需要先判断符合要求,才对符合进行赋值
if (tnear > 0 && u > 0 && v > 0 && 1 - u - v > 0) {
    return true;
}
return false;

这里需要注意的是:最后需要对值的正负进行判断

5. 结果和分析

  仔细观察地面上会有几个小蓝点,这是由于相交时浮点数运算误差造成的,读者可以自行进行完善和修复

posted @ 2024-01-20 12:26  Kellen_Gram  阅读(468)  评论(0)    收藏  举报