下面我用“从数学到像素”的视角,把 GPU 中栅格化(Rasterization)和纹理处理(Texture Processing)的核心原理讲清楚,尽量避开纯硬件细节,但保留关键思想。
一、GPU 渲染的整体流程(先建立全局观)
一个三角形最终变成屏幕上的像素,大致经历:
顶点处理 → 图元装配 → 光栅化 → 片元处理 → 输出合并- 栅格化:决定“哪些像素被三角形覆盖”
- 纹理处理:决定“这些像素长什么样(颜色)”
二、栅格化(Rasterization)原理
1️⃣ 栅格化在做什么?
一句话定义:
把连续的几何图形(三角形)
转换为离散的屏幕像素(片元 / fragments)
本质是一个“点-三角形包含性测试” 问题。
2️⃣ 输入是什么?
顶点着色器输出后,每个三角形有三个屏幕空间坐标:
(x1, y1, z1, w1) (x2, y2, z2, w2) (x3, y3, z3, w3)经过透视除法后得到:
(x/w, y/w, z/w)此时坐标是屏幕像素坐标。
3️⃣ 核心思想:扫描线 + 重心坐标
✅ 方法一:扫描线算法(传统)
- 找到三角形覆盖的包围盒
- 对每一行(scanline):
- 计算与三角形边的交点
- 填充交点之间的像素
+--------+--------+ | | | | #### | ###### | | ######|####### | +--------+--------+✅ 方法二:重心坐标(现代 GPU 主流)
对每一个像素点 P,计算:
P = α·A + β·B + γ·C其中:
- α, β, γ 是重心坐标
- 判断条件:
α ≥ 0 且 β ≥ 0 且 γ ≥ 0✅ 满足 → 像素在三角形内
❌ 否则 → 丢弃
优点:
- 天然支持插值(颜色、法线、UV)
- 非常适合并行计算
4️⃣ 属性插值(非常关键)
一旦知道重心坐标,就可以插值:
color = α·cA + β·cB + γ·cC uv = α·uvA + β·uvB + γ·uvC depth = α·zA + β·zB + γ·zC👉这就是片元着色器的输入来源
5️⃣ 早期深度测试(Early Z)
现代 GPU 在栅格化阶段就做:
- 深度预测试
- 被遮挡的片元直接丢弃
✅ 节省大量片元着色计算
三、纹理处理(Texture Processing)原理
1️⃣ 纹理的本质
纹理 =一块连续的图像数据
常见形式:
- 2D 纹理(最普遍)
- Cube Map
- 3D 纹理(体渲染)
2️⃣ 纹理坐标(UV)
每个片元有:
(u, v) ∈ [0, 1]GPU 把它映射到纹理像素(texel):
x = u × (width - 1) y = v × (height - 1)3️⃣ 纹理采样(Texture Sampling)
✅ 最近邻采样(Nearest)
取最近的 texel- 快
- 锯齿明显
✅ 双线性插值(Bilinear)
取 2×2 的 texel:
P = lerp( lerp(T00, T10, fx), lerp(T01, T11, fx), fy )✅ 平滑
✅ 实时渲染主流
✅ 三线性过滤(Trilinear)
在mipmap 层之间再做一次插值
lerp( bilinear(level n), bilinear(level n+1), fz )✅ 消除远处闪烁
4️⃣ Mipmap(核心加速技术)
提前生成:
Level 0: 原图 Level 1: 1/2 Level 2: 1/4 ...GPU 根据屏幕像素密度自动选择层级
✅ 减少缓存 miss
✅ 防止摩尔纹
5️⃣ 纹理缓存与局部性
纹理访问特点:
- 空间局部性强
- 顺序访问多
GPU 使用:
- 纹理缓存(Texture Cache)
- 2×2 像素 quad 同时采样(用于导数计算)
6️⃣ 各向异性过滤(Anisotropic Filtering)
当纹理以“倾斜角度”观察时:
- 普通 mipmap 会模糊
- 各向异性过滤:
- 沿拉伸方向采样更多 texel
✅ 画质显著提升
四、栅格化 + 纹理处理的协同流程
三角形 ↓ 光栅化(生成片元) ↓ 插值 UV / 颜色 / 深度 ↓ 片元着色器 ↓ 纹理采样(访存) ↓ 深度测试 ↓ 写入 Framebuffer五、为什么 GPU 特别适合这件事?
特性 | 原因 |
|---|---|
高度并行 | 每个像素独立 |
SIMD / Warp | 同时处理 2×2 像素 |
专用纹理单元 | 硬件插值 + 过滤 |
深度/模板单元 | 硬件 early-Z |
Tile-Based | 移动 GPU 优化带宽 |
六、一句话总结
栅格化决定“画哪里”
纹理处理决定“画成什么样”