第三课 深度缓冲

Reference

Lesson 3: Hidden faces removal (z buffer) · ssloy/tinyrenderer Wiki (github.com)
深度测试-透明测试 | AncientElement (gitee.io)

深度缓冲

在上一节中我们已经加载了模型,但是可以发现模型的嘴巴和眼睛被模型后面的空腔覆盖了。

在obj文件中实际上已经存在了每个顶点的z值,但是在上一课我们没有去使用,这里我们用到z_buffer算法来记录每个像素的深度值

当有距离更加近的元素时更新z_buffer中对应的像素的深度值,并且更新颜色值

但是这里只有顶点的z值,没有每一个像素的z值,怎么办呢?

使用上节课的重心坐标,用计算出来的 $\alpha$ $\beta$ $\gamma$ ,可以对三个顶点进行插值

重心坐标有多种计算方法,除了上节课中的叉乘法,还有面积法

这里我们继续用上节课的方法。

在三角形光栅化的函数中添加一个z_buffer参数,用于读取深度值和写入

1
void triangle_foreach(Vec3f* pts, float* z_buffer, TGAImage& image, TGAColor color)

并且得到 $\alpha$ $\beta$ $\gamma$ ,如果在三角形内则计算深度值。

1
2
3
4
5
6
7
Vec3f uvw = barycentric(pts[0], pts[1], pts[2], point);
if (uvw.x < 0 || uvw.y < 0 || uvw.z < 0) continue;
point.z = 0;
//用重心坐标计算当前像素的z值
point.z += pts[0].z * uvw.x;
point.z += pts[1].z * uvw.y;
point.z += pts[2].z * uvw.z;

进行深度测试,通过(较于深度缓冲中的值距离相机更加)则写入深度值,并且写入颜色

因为这里我们的相机在z的负半轴,所以z越大反而越接近相机。

1
2
3
4
5
6
7
8
int index = int(point.x + point.y * width);
//这里z轴是负反向 越大越近
if (z_buffer[index] < point.z) //当前z_buffer的深度值在后面更新设深度值 更新颜色
{
//std::cout << point.z << std::endl;
z_buffer[index] = point.z;
image.set(point.x, point.y, color);
}

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//遍历法 深度缓冲
void triangle_foreach(Vec3f* pts, float* z_buffer, TGAImage& image, TGAColor color) {
//包围盒
int width = image.get_width();
int height = image.get_height();
Vec2f left_down(width - 1, height - 1);
Vec2f right_up(0, 0);
for (int i = 0; i < 3; i++)
{
left_down.x = std::min(left_down.x, pts[i].x);
left_down.y = std::min(left_down.y, pts[i].y);

right_up.x = std::max(right_up.x, pts[i].x);
right_up.y = std::max(right_up.y, pts[i].y);
}
//遍历包围盒
Vec3f point;
for (point.x = left_down.x; point.x <= right_up.x; point.x++)
{
for (point.y = left_down.y; point.y <= right_up.y; point.y++)
{
Vec3f uvw = barycentric(pts[0], pts[1], pts[2], point);


if (uvw.x < 0 || uvw.y < 0 || uvw.z < 0) continue;

point.z = 0;

//用重心坐标计算当前像素的z值
point.z += pts[0].z * uvw.x;
point.z += pts[1].z * uvw.y;
point.z += pts[2].z * uvw.z;

int index = int(point.x + point.y * width);
//这里z轴是负反向 越大越近
if (z_buffer[index] < point.z) //当前z_buffer的深度值在后面更新设深度值 更新颜色
{
//std::cout << point.z << std::endl;
z_buffer[index] = point.z;
image.set(point.x, point.y, color);
}
}
}
}