这里回顾GAMES101 HW2,这次作业的内容是Triangles and Z-buffering。

课程主页:

课程作业:

课程视频:

参考资料:

整体流程

算法的整体流程如下:

  • for (each triangle T)
    • for (each sample (x,y,z) in T)
      • if (z < zbuffer[x,y]) // closest sample so far
        • framebuffer[x,y] = rgb; // update color
        • zbuffer[x,y] = z; // update depth
      • else
        ;
        • // do nothing, this sample is occluded

我们需要实现的函数为insideTriangle和rasterize_triangle。

insideTriangle

判断点$P$是否在三角形$ABC$内的方法如下($\times $表示叉积):

注意我们需要的是叉积的$z$分量,所以$A,B,C,P$的$z$分量可以取任意值,整体代码如下:

bool judge(Eigen::Vector3f &p, Eigen::Vector3f &a, Eigen::Vector3f &b) {
    Eigen::Vector3f v = (b - a).cross(p - a);

    return v.z() > 0;
}

static bool insideTriangle(float x, float y, const Triangle* t)
{   
    // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
    // 获得3个顶点
    Eigen::Vector3f p(x, y, 0.0);
    Eigen::Vector3f a = (t->v)[0];
    Eigen::Vector3f b = (t->v)[1];
    Eigen::Vector3f c = (t->v)[2];

    if (judge(p, a, b) && judge(p, b, c) && judge(p, c, a)) {
        return true;
    }

    return false;
}

rasterize_triangle

实现之前的伪代码即可,这里将ssaa合并,利用参数$n$控制super-sampling,$n=1$即为默认情形,于是伪代码如下:

算法的整体流程如下:

//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    // 转换为齐次坐标
    auto v = t.toVector4();
    
    // TODO : Find out the bounding box of current triangle.
    // bounding box
    float x_min = 1e10;
    float x_max = -1e10;
    float y_min = 1e10;
    float y_max = -1e10;
    for (int i = 0; i < 3; i++) {
        x_min = std::min(x_min, t.v[i](0));
        x_max = std::max(x_max, t.v[i](0));
        y_min = std::min(y_min, t.v[i](1));
        y_max = std::max(y_max, t.v[i](1));
    }
    int x_min_int = int(x_min);
    int x_max_int = int(x_max) + 1;
    int y_min_int = int(y_min);
    int y_max_int = int(y_max) + 1;
    std::cout << x_min_int << " " << x_max_int << " " << width << std::endl;
    std::cout << y_min_int << " " << y_max_int << " " << height << std::endl;
    
    // ssaa
    float x, y;
    // 间距
    float d = 1.0 / n;
    for (int i = x_min_int; i <= x_max_int; i++) {
        for (int j = y_min_int; j <= y_max_int; j++) {
            // 记录super-sample在三角形内的数量
            int cnt = 0;
            // 记录块内的最小深度
            float z_interpolated_min = 1e10;
            // 遍历每个super-sample
            for (int p = 0; p < n; p++) {
                for (int q = 0; q < n; q++) {
                    x = i + (p + 0.5) * d;
                    y = j + (q + 0.5) * d;
                    // 在三角形内则更新cnt
                    if (insideTriangle(x, y, &t)) {
                        cnt++;
                        // 计算插值深度值
                        auto[alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
                        float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                        float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                        z_interpolated *= w_reciprocal;
                        z_interpolated_min = std::min(z_interpolated_min, z_interpolated);
                    }
                }
            }
            int index = get_index(i, j);
            // 越小越近
            if (z_interpolated_min < depth_buf[index]) {
                // 设置颜色
                Eigen::Vector3f point(i, j, 0);
                set_pixel(point, t.getColor() * cnt / (n * n));
                // 更新深度
                depth_buf[index] = z_interpolated_min;
            }
        }
    }
}

编译并运行,其中2, 3表示传入的$n$的值(不传入为1):

make -j4
./Rasterizer origin.png
./Rasterizer ssaa2.png 2
./Rasterizer ssaa3.png 3

结果展示:

origin.png:

ssaa2.png:

ssaa3.png: