开场
图形系统的目标是把应用程序给出的场景描述转换成屏幕上的图像。应用程序通常负责指定物体、材质、观察者和光源;底层系统负责把这些描述一步一步处理成像素。
图形管线(Graphics Pipeline)就是这种处理流程的核心抽象。它把复杂的成像过程拆成多个阶段,每个阶段接收上一阶段的输出,再生成下一阶段需要的数据。现代图形硬件可以把许多阶段放到 GPU 上并行执行,因此它特别适合实时渲染。
图形系统和 API
应用程序不应该直接控制图形硬件的每个细节。更合理的方式是通过应用程序编程接口(Application Programming Interface,API)描述“要画什么”,由图形系统决定“怎么画”。
API 通常提供几类信息的入口。
| 信息 | 作用 |
|---|---|
| 物体 | 指定点、线段、多边形、曲线或曲面 |
| 观察者 | 指定相机位置、朝向、视口和投影方式 |
| 光源 | 指定光的位置、方向、颜色和强度 |
| 材质 | 指定表面对光的吸收、漫反射、镜面反射等性质 |
| 输入与系统能力 | 处理鼠标、键盘、显示设备和硬件功能 |
这种分层让应用开发者专注于场景组织,也让图形系统可以独立优化底层执行方式。
两种成像思路
从物理角度看,可以用光线追踪(Ray Tracing)模拟成像。它从投影中心或相机出发追踪光线,判断光线碰到哪些物体、发生反射还是折射,最后计算颜色。光线追踪适合处理全局效果,例如多次反射、透明物体和复杂阴影,但计算成本较高,而且通常需要在计算时访问完整场景数据。
实时图形系统常用另一种更实用的思路:按照应用程序生成物体的顺序,一个物体接一个物体处理。它通常重点处理局部光照,不完整模拟物体之间的复杂光传输。这个取舍牺牲了一部分物理真实性,但换来了更稳定的实时性能。
管线总览
一个基本图形管线可以概括成以下阶段。
每个阶段处理的数据粒度不同。顶点处理关注点的位置和属性;图元组装把点组织成线段、多边形、曲线或曲面;光栅化把几何对象覆盖到像素网格上;片元处理决定每个候选像素最终写入什么颜色。
顶点处理
顶点处理(Vertex Processing)的大量工作是坐标变换。一个点在建模时通常先处在物体自己的局部坐标系中;进入场景后,需要转换到世界坐标系;相机观察时,又需要转换到相机坐标系;最后投影到屏幕坐标系。
这些坐标变化通常都可以表示成矩阵变换。矩阵形式的好处是统一、可组合,并且非常适合 GPU 并行计算。顶点处理阶段还可以计算或传递顶点颜色、法向量、纹理坐标等属性。
投影
投影(Projection)把三维观察者和三维物体结合起来,生成二维图像所需的位置关系。常见投影可以分成透视投影(Perspective Projection)和平行投影(Parallel Projection)。
| 投影方式 | 几何特征 | 常见效果 |
|---|---|---|
| 透视投影 | 投影线汇聚到投影中心 | 近大远小,接近人眼和相机效果 |
| 平行投影 | 投影线彼此平行 | 保留平行关系,常用于工程和技术图 |
透视投影更适合表达真实视觉经验;平行投影更适合强调尺寸和结构关系。两者都不是“更正确”的唯一选择,而是服务于不同任务。
图元组装和裁剪
图元组装(Primitive Assembly)把顶点收集成几何对象。API 传入的顶点本身只是点的位置和属性,只有按照指定规则组合后,系统才知道它们构成线段、三角形、多边形、曲线还是曲面。
裁剪(Clipping)处理的是可见范围问题。真实相机不能看到整个世界,虚拟相机也只能看到视锥体或观察体积内的对象。完全在可见范围外的对象会被移除;部分在范围内的对象需要被切割,只保留可见部分。
裁剪的目的不是改变物体本身,而是限制后续阶段只处理相机可能看到的部分,从而避免无意义计算。
光栅化
光栅化(Rasterization)把几何图元转换成片元(Fragment)。片元可以理解为“潜在像素”:它有帧缓冲区中的位置,也带有颜色、深度、纹理坐标等属性,但它还不一定会成为最终像素。
例如,一个三角形投影到屏幕后,会覆盖一片像素位置。光栅化阶段要判断哪些像素位置被这个三角形覆盖,并把顶点属性插值到内部位置。后续学习重心坐标时,会更清楚地看到这种插值是如何计算的。
片元处理和深度缓冲
片元处理(Fragment Processing)负责决定片元最终的颜色。颜色可以来自顶点颜色插值,也可以来自纹理映射(Texture Mapping)和光照计算。
但是,一个片元覆盖某个屏幕位置,并不意味着它会成为最终可见像素。场景中可能有多个物体投影到同一个像素位置,离相机更近的片元应该挡住更远的片元。深度缓冲(Depth Buffer 或 Z-buffer)就是为了解决这个问题。
深度缓冲通常为每个像素保存一个深度值。片元处理时,系统比较新片元深度和已有深度,只有更接近相机的片元才会更新颜色。
纹理映射
纹理映射(Texture Mapping)把二维图像贴到三维几何表面上。几何模型提供形状,纹理提供表面细节。这样可以在不显著增加几何复杂度的情况下,让物体看起来更丰富。
一个模型可以只有较少的多边形,但通过纹理表现颜色变化、图案、污渍、木纹或鳞片等细节。实际渲染时,顶点会携带纹理坐标,光栅化阶段把纹理坐标插值到片元位置,片元处理阶段再根据纹理坐标读取纹理颜色。
可编程管线
早期图形管线的很多操作是固定的,称为不可编程管线(Non-programmable Pipeline)。这种设计适合硬件资源有限的时代,因为数据通常按简单统一的方式处理,但它限制了开发者表达复杂效果的能力。
现代图形管线把顶点处理器和片元处理器开放给程序控制,形成可编程管线(Programmable Pipeline)。顶点着色器(Vertex Shader)可以改变顶点位置和顶点属性;片元着色器(Fragment Shader)可以按片元计算纹理、光照和材质效果。
组装、裁剪、光栅化] C --> D[片元着色器] D --> E[输出合成] E --> F[屏幕]
可编程管线并没有取消管线结构,而是在关键阶段给开发者更多控制权。实时渲染中的许多视觉效果,例如位移映射、逐片元光照、风格化渲染和后处理,都依赖这种可编程能力。
小结
图形管线把三维场景生成二维图像的过程拆成一组明确阶段。应用程序通过 API 指定对象、相机、光源和材质;管线依次完成坐标变换、投影、图元组装、裁剪、光栅化、片元处理和深度测试。理解这条链路后,后续学习几何、光照、纹理和着色器时,就能知道每个概念在系统中的位置。