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}")
|