Home Forums GAMES在线课程(现代计算机图形学入门)讨论区 关于作业2两个三角形重叠部分的Z-Buffer问题 Reply To: 关于作业2两个三角形重叠部分的Z-Buffer问题

#12095 Score: 0
shs1992shs
Participant

我的理解是mvp变换后z坐标就在[-1,1]之内,那么经过放大f1倍后(vert.z()*f1),如果z值是负的,就是还是负的。第二个三角形原始的z为-5,小于第一个三角形原始z值-2,mvp变换后还是符合这种情况。
那么加上f2之后,就是所有点都平移到了z轴正方向,此时所有的z坐标都是正值,那么此时第二个三角形的z值假设由最初的-5变为1,第一个三角形的z值此时必然比1要大,可能为8,还是符合靠近相机的z值大于远离相机的z值,这会覆盖后面的z值。那么判断深度值,坚持以这个关系进行比较,即若当前的z值大于z缓冲,那么就以当前的三角形的颜色绘制,并更新z缓冲,这样理解起来是非常直观的。
此处出现问题,主要是代码框架的depth_buf初始化的问题,初始化值是正无穷大, 那么从第一个三角形开始比较,其z值小于初始 z缓冲(无穷大),认为在目前深度后面,不用更新,那么最终就什么都没有绘制,为黑屏。
如果直接把判断关系改为作业提示中的z值越大表示离视点越远,即若当前z值小于z缓冲,则以当前颜色绘制并更新,那么此时第一个三角形满足情况,顺利绘制并更新z缓冲。但是第二个三角形中的部分z值小于和第一个三角形重叠部分的z值,即小于z缓冲值,那么满足判断关系,进行绘制覆盖并更新,从而颠倒了两个三角形的绘制顺序。所以最简单的修改,就是把初始化的z缓冲设为负无穷大,这样就是z值越小,离相机越远。参考下面的代码。
如果坚持以作业提示中的方法,初始化的z缓冲还是正无穷大,那就需要修改原代码框架里的视口变换的z坐标变换了,即把 vert.z() * f1进行放大时,前面加一负号,完成z值镜像反转。此时原来离视点最远的z值由负变正,数值上变为最大,那么判断关系就符合作业要求了,z值越小离视点越近,若当前z值小于z缓冲,则绘制覆盖并更新,顺序就正确了。这个方法我认为理解起来不是那么直观,因为前面的mvp推导过程都是坚持z值为负值的,突然一下转为正值,很容易让人混淆,理解起来困难。
两种方法都试一下之后,相信就会加深理解了。

修改后的代码如下:

int pixel_index = get_index(x, y);
float depth = depth_buf[pixel_index];
if (depth < z_interpolated)  {
    set_pixel(Vector3f(x, y, z_interpolated), t.getColor());
    depth_buf[pixel_index] = z_interpolated; // update depth
}
void rst::rasterizer::clear(rst::Buffers buff)
{
    if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
    {
        std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
    }
    if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
    {
        //std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity()); //原框架初始深度值为∞
        std::fill(depth_buf.begin(), depth_buf.end(), -std::numeric_limits<float>::infinity());//初始深度值改为-∞,z轴最远处。
    }
}

修改视口变换的代码:

// Viewport transformation
for (auto &vert : v)
{
    vert.x() = 0.5 * width * (vert.x() + 1.0);
    vert.y() = 0.5 * height * (vert.y() + 1.0);
    vert.z() = -vert.z() * f1 + f2; // 负号是为了实现镜像反转(reflection )
    // vert.z() = vert.z() * f1 + f2;
}