首页/在线代码库/demo14_rasterizer.py

demo14_rasterizer.py

手写一个小型三角形光栅化器: 把两个前后重叠的三角形投影后画到像素网格, 用 Z-Buffer 保证"离相机近的像素覆盖远的", 展示遮挡如何被解决。

Python 67 行 系列:核心讲义 🔒 源码仅供在线学习,不提供文件下载
完整源码 · 在线查看demo14_rasterizer.py
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
"""演示14: 光栅化 + 深度缓冲 (Z-Buffer)。

手写一个小型三角形光栅化器: 把两个前后重叠的三角形投影后画到像素网格,
用 Z-Buffer 保证"离相机近的像素覆盖远的", 展示遮挡如何被解决。
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from figure_style import figures_dir, configure

configure()

OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)

W, H = 120, 120
depth = np.full((H, W), np.inf)     # 深度缓冲: 初始为无穷远
color = np.zeros((H, W, 3))


def project_ortho(x, y, z):
    """正交投影 + 简单缩放平移到屏幕坐标。"""
    sx = int((x * 40 + 60)); sy = int((-y * 40 + 60))
    return sx, sy


def rasterize_triangle(v, col, z_plane):
    """把 3D 三角形(顶点带 z)光栅化, 用 z-buffer 决定可见性。"""
    pts = np.array([project_ortho(*p) for p in v])
    xmin, xmax = max(0, pts[:, 0].min()), min(W - 1, pts[:, 0].max())
    ymin, ymax = max(0, pts[:, 1].min()), min(H - 1, pts[:, 1].max())
    for y in range(ymin, ymax + 1):
        for x in range(xmin, xmax + 1):
            if point_in_triangle((x, y), pts):
                # 该像素的深度 = 三角形平面 z 值(近似用平面平均)
                z = z_plane
                if z < depth[y, x]:          # 深度测试: 更近才覆盖
                    depth[y, x] = z
                    color[y, x] = col


def point_in_triangle(p, tri):
    """重心坐标法判断点是否在三角形内。"""
    (x, y) = p
    (x1, y1), (x2, y2), (x3, y3) = tri
    det = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)
    a = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / det
    b = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / det
    c = 1 - a - b
    return a >= 0 and b >= 0 and c >= 0


# 两个重叠三角形: 红色在前(z=0.5), 蓝色在后(z=2.0)
rasterize_triangle([(0.4, -0.8, 0.5), (0.8, 0.8, 0.5), (-0.8, 0.6, 0.5)],
                   [1.0, 0.2, 0.2], 0.5)
rasterize_triangle([(-0.9, -0.9, 2.0), (0.9, -0.5, 2.0), (0.0, 0.9, 2.0)],
                   [0.2, 0.4, 1.0], 2.0)

fig, ax = plt.subplots(figsize=(6, 6))
ax.imshow(color, origin="lower", extent=[-1.5, 1.5, -1.5, 1.5])
ax.set_title("光栅化 + Z-Buffer: 近处红色三角遮挡远处蓝色三角")
ax.set_xlabel("屏幕 X"); ax.set_ylabel("屏幕 Y")
fig.tight_layout()
out = os.path.join(OUT, "fig_cg_raster.png")
fig.savefig(out, dpi=150)
print(f"已光栅化 {(color > 0).sum(axis=2).max()} 层像素 -> {out}")

输出图

fig_cg_raster.png
输出图 · fig_cg_raster.png