- 发布于
计算机图形学中的变换与光栅化
AI 辅助翻译自英文阅读英文原文
- 作者

- 姓名
- Garfield Zhu
- @_AlohaYo_
@Author: Garfield Zhu
作为计算机图形学中最基础的概念,变换与光栅化是将图形内容显示为图像格式时的基本任务。
让我们先了解这些基础知识,迈入图形学的大门。
变换
基本二维变换
缩放
沿 x 轴缩放图像

缩放后每个点 (x0, y0) 变为 (x1, y1),有:
"y1 = y0"
"x1 = x0 * 0.5 "
于是定义缩放矩阵 S(x,y):

本例中 Sx = 0.5,Sy = 1。
旋转
旋转图像(以原点为中心)

设旋转矩阵 R(θ) 满足:

证明:
(1, 0) becomes (cosθ, sinθ)

(0, 1) becomes (-sinθ, cosθ)

因此得到 R 的定义:

线性变换
缩放和旋转都可以表示为矩阵的线性形式。
线性变换


平移
平移就是在坐标系中移动对象。


齐次坐标
平移不同于线性变换。现在需要两个矩阵才能将它们组合起来:
同时包含线性变换和平移的矩阵。


但我们希望用一个矩阵同时表示它们。为此引入新工具:齐次坐标。
定义
简单来说,齐次坐标通过为矩阵增加一个维度,描述投影等会产生高维结果的变换。换句话说,n 维空间使用 n+1 维矩阵描述。
这里讨论二维变换,因此用三元形式表示:
- 2D point: (x, y, 1)
- 2D vector: (x, y, 0)
相应地,变换矩阵也是三维矩阵:
齐次坐标中的变换矩阵:

齐次变换具有以下特性:
- 对向量而言,
w的值不会改变。
平移向量:

- Vector + Vector = Vector
- [x1, y1, 0] + [x2, y2, 0] = [x1 + x2, y1 + y2, 0]
- Point - Point = Vector
- [x1, y1, 1] - [x2, y2, 1] = [x1 - x2, y1 - y2, 0]
- Point + Vector = Point
- [x1, y1, 1] + [x2, y2, 0] = [x1 - x2, y1 - y2, 1]
仿射变换
应用齐次矩阵后得到的变换称为“仿射变换”。
仿射变换:

借助该工具,可以用齐次形式表示上述三种变换:

当然,三种变换也可以合并为一个。
变换链
矩阵乘法按左结合计算,例如 Matrix_1 x vector。
计算顺序是从右到左:
M4 x M3 x M2 x M1 x vector
顺序为 M1、M2、M3、M4……也可以依据结合律计算。
(M4 x M3 x M2 x M1) x vector
-> M5 x vector
实践
二维仿射变换矩阵非常实用。在 UI 绘制元素时,通常传入该矩阵来应用变换。
在三维变换矩阵中,第三行始终是 [0, 0, 1],无需作为参数传入。
因此二维绘图函数中的变换矩阵通常写作:
[A, B, C, D, Tx, Ty]
- PDF 变换:
Rect.transform(new Matrix(A, B, C, D, Tx, Ty)) - SVG 变换:
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"> <rect x="10" y="10" width="30" height="20" fill="red" transform="matrix(3 1 -1 3 30 40)" /> </svg>
三维变换
三维仿射变换与二维类似,只需将变换矩阵扩展为四维。
观察变换
视口变换
投影变换
透视变换
待定
光栅化
分辨率
图像分辨率
以不同分辨率渲染的图像

显示分辨率
显示分辨率标准:

原生分辨率 显示设备的原生分辨率由固定的物理像素决定。在固定的原生分辨率下,可以设置相同或更低的显示分辨率,也可以显示更低分辨率的图像;这本质上是缩放。
显卡将 800×600 图像发送到 1366×768 LCD 时,显示器必须插值(缩放)图像以填满屏幕。此例中宽高比(800×600 为 4:3,1366×768 为 16:9)不同,因此图像不仅会放大,还会变形。
- 位图受影响很大,矢量图表现更好。
- 原生分辨率越高,缩放的副作用越不明显。
- 建议将原生分辨率作为显示分辨率。
发送到 1024768 原生分辨率的 800600 内容。
屏幕空间
数字屏幕空间用二维矩阵描述,一个单位表示一个像素。

子像素布局
一个像素由 RGB 子像素组成,不同显示设备的条纹布局不同。

P30 Pro

Google Nexus One

光栅(位图)与矢量图
放大光栅图与矢量图
光栅图(位图)
位图结构示例:
光栅图形或位图是最常见的图形表示形式,采用点阵数据结构。
典型数据结构:固定大小的二维矩阵
显示算法:缩放
典型格式:Jpeg、Png、WebP、Bmp、PPM 等
用途:照片、图像、画布。
位图示例:
原始文本 PPM 格式图像:对应的 PNG:

矢量图
矢量图形通过笛卡尔平面上的点定义。
典型数据结构:可绘制对象,如点、线、矩形、圆、曲线等。
显示算法:变换、光栅化
典型格式:SVG、EPS、PDF 等
用途:几何图形、印刷、徽标、图标、插画。
SVG 示例:
W3C SVG 文件示例:
对比
何时使用?
- 对于高分辨率显示,矢量图形更高效;光栅图在高分辨率下可能很大。
- 对于大量对象,光栅图形更高效。矢量图需要将每个对象存储为模型,而光栅图只存储整个画布。
SVG 与 canvas
SVG,即 Scalable Vector Graphics(可缩放矢量图形)。
- 它是 W3C 自 1999 年制定的基于 XML 的矢量图像格式。
- 基于 XML,同时属于 DOM,可以直接操作。
Canvas 元素。
- 它是 HTML5 标准的一部分,支持动态、可脚本化地渲染二维图形和位图(Apple 于 2004 年在 WebKit 中初始化)。
- 基于脚本,绘制完成后浏览器不会持续关注它。
- 操作元素后需要重新绘制。
性能对比:

基础光栅化
什么是光栅化?它是一种采样。
举个简单例子:想象彩色墨水漂浮在水面上形成画面。将屏幕窗口沉入水中再慢慢取出,就得到一幅画,这与光栅化类似。
Bresenham 直线算法 是把线(基本图元、矢量)绘制成位图的最基础光栅化算法。
Bresenham 直线算法结果示意。
第 1 步:
将几何图形(如三角形)放入屏幕空间。

第 2 步:
采样:判断每个像素(像素空间中心)位于三角形内还是外。

第 3 步:
使用包围盒缩小采样区域。

第 4 步:
取得三角形内的像素。

第 5 步:
显示这些像素的颜色以呈现三角形。

现在得到了一幅光栅化图形,但锯齿明显。提高分辨率可以改善效果(参见
MSPaint)。
抗锯齿
原理
糟糕的“采样”:
抗锯齿本质上是采样;对锐利图像进行低频采样会产生锯齿或摩尔纹。

采样频率如何丢失原始信号:
分辨率不够高时,会看到“模糊”的图像。


采样前先模糊图像:
如果分辨率不够高,就会看到“模糊”的图像。


抗锯齿方法
使用低通滤波器滤除高频信号:
使用卷积(或对单元内颜色取平均)。

SSAA/MSAA(超采样/多重采样抗锯齿):
在单个像素中使用更高的采样比率。

比较不同采样效果。
在像素中使用单次采样:

在像素中使用多重采样:

通过卷积/平均降至低分辨率。

字体光栅化
参见 Wiki 上的字体光栅化。
如今字体是矢量图,可以从极小缩放到极大。字体光栅化就是将矢量路径映射到像素矩阵。

未经过抗锯齿的原始光栅字体:
示例
字体抗锯齿
不同的抗锯齿级别
不带 hint 的基础抗锯齿
示例

带 hint 的抗锯齿
示例

RGB 平板上的子像素渲染
示例

在屏幕文字显示中,字体 hinting 指定需要插值的基础像素,从而更清晰地渲染字体。
-> 字体编辑器可以自动执行 hinting。
-> 高质量商业字体通常会手动 hinting,以获得最清晰的外观。
示例

使白色显示的子像素渲染
示例

现代字体光栅化
如今同时支持 Windows 和 macOS 的主流字体光栅化库是 FreeType。
FreeType 中采用子像素渲染的小字号字符“e”。
子像素渲染的“e”
