https://haqr.eu/tinyrenderer/z-buffer/
不丢弃任何一个三角形,将其全部绘制出来。不过按照从后到前的顺序来绘制,那么前面的三角形会覆盖掉后面的三角形。这种技术被称为“画家算法”。
缺点
计算成本非常高,每次都要排序
结果的正确性无法得到保证,例如三角形彼此重叠
(其实解决的方案很简单,我们没有必要按照三角形排序,直接单像素处理就可以了。
遍历所有的三角形,然后涂色每个三角形中的所有像素。由于这些三角形没有经过正确的排序,因此会出现视觉上的瑕疵。叫深度插值的原因是只有三角形的顶点有深度值 (z),三角形内的需要使用上一节学到的重心坐标的方式插值。
for each triangle t:
for each pixel p that t covers:
compute its depth z
update the depth buffer with z
paint the pixel
我先直接绘制插值了的深度(不做额外处理),这里很简单,就只是额外创建一幅 ppm 图,然后里面使用重心坐标插值的 z 作为颜色就可以。观察左图可以看到 “混乱” 的深度图。本应该越近越亮,说明了我们前后关系的错误 (例如尾巴的正面在腿前面)。
// Random color to framebuffer image
set_pixel(framebuffer, x, y, color);
// Depth interplotion to zbuffer image
depth := (alpha * a.z + beta * b.z + gamma * c.z) / 255.0;
set_pixel(zbuffer, x, y, Vector4.{depth, depth, depth, 1});


BUG 小插曲:意外得到的 故障效果。
我计算三角形面积的时候乘 0.5 的时候忘了加括号。导致AABB 光栅化三角形时候出错。
接下来实现正确的 z-buffer,不过首先给我们自己写的 ppm.jai 加一个 get_pixel 返回 [x, y] 位置的像素颜色。这非常简单。
PPM_Image :: struct {
width: int;
height: int;
pixels: [] Vector4;
}
get_pixel :: (image: *PPM_Image, x: int, y: int) -> Vector4 {
// 我这里分开写 assert,如果不小心越界了,是编译期错误,
// 我可以清楚知道到底是哪一个,到底是怎么越界的。
assert(x >= 0);
assert(x < image.width);
assert(y >= 0);
assert(y < image.height);
index := y * image.width + x;
return image.pixels[index];
}


Z-Buffer 的实现非常简单,只需要做一个判断。我这里的 Z 是颜色,值越大深度图颜色越浅 (因为 0xFFFFFF 是白色, 0x000000 是黑色嘛,RGB 什么光都没有,就黑啦,白光就是所有频率混合一起(老实说,身体,或者说大脑的这种视觉处理方式挺有趣的,就像是水是无味道一样。))。
draw_triangle :: (framebuffer: *PPM_Image, zbuffer: *PPM_Image, a: Position, b: Position, c: Position, color : Vector4) {
aabb := get_aabb(a, b, c);
abc_area := signed_triangle_area(a, b, c);
if abc_area == 0 return;
for y: aabb.y_min .. aabb.y_max-1{
for x: aabb.x_min .. aabb.x_max-1 {
p := Position.{x, y, 0};
alpha := signed_triangle_area(p, a, b) / abc_area;
beta := signed_triangle_area(p, b, c) / abc_area;
gamma := signed_triangle_area(p, c, a) / abc_area;
// negative barycentric coordinate => the pixel is outside the triangle
if alpha < 0 || beta < 0 || gamma < 0 {
continue;
}
z := (alpha * a.z + beta * b.z + gamma * c.z) / 255.0; // Depth interplotion
if z <= get_pixel(zbuffer, x, y).z continue;
set_pixel(zbuffer, x, y, Vector4.{z, z, z, 1});
set_pixel(framebuffer, x, y, color);
}
}
}