光影箱庭

0 前言

0.1 第一版

Long time no see!

一直想开图形学的坑了。虽然本地服务器(雾)里存有另一份非常简略的笔记,但是写得不是非常好,实质上就是 Copy and Paste。

之后我期望能每周至少更新一次?虽然大概不能保证一次更多少……顺带一提,刚开始很可能会拖一段时间,因为众所周知书的最开头一般都没什么可写的(

0.2 第二版

我发觉好像如果仅限于《Fundamentals of Computer Graphics》,写起来不是很有意思,毕竟那样不就和第一版前言里说的一样,都是 C&P 嘛。所以之后我就打算 Extend 这一份笔记的内容啦,可能会参考一下一些之前看过的(比如 THU 出版社的那本《计算机图形学基础》就是我第一版吐槽的那份笔记,还有 RT in One Week。可能还会写点 CS231a,Games101 之类的?)。

既然换主题就要改个名字。最开始我想写 Cornell Box 的,但是本来这篇也没打算写成英文。直译成康奈尔盒也不是很好。所以用了现在这个。箱庭这个词最早看到是在地错的小说里,Box 正好对应“箱”的概念。而且现在很多游戏在一些养成内容中也会引入“箱庭”的概念。计算机图形学虽然并不是只有“光”与“影”(比如 draw_line 这种 dirty work),但光影确实是其中最重要的一部分,至少是将图形呈现给用户时最重要的一部分。我个人觉得还算比较契合吧。

图片引自 知乎 TecrayC:Computer graphics 专栏。

1 引入

1.1 简介

计算机图形学,顾名思义,是一门研究计算机中的图形的学科。简单地分个类,图形学可以分为建模、渲染、动画三大块。

  • 建模:我如果想在计算机中存储一个物体的形貌,便需要一套约定好的数据——模型来表述它。模型的表示方式有很多,例如三角片(三角剖分)、点云(以点代面)、辐射场(给定位置与视角,返回对应体素的光辐射信息)。我一般将其理解为,将(现实世界的)图形输入到计算机中。(我们这里说的模型一般指三维模型)
  • 渲染:与建模相对应,渲染研究的是如何让计算机中的模型输出为(现实世界的)图形。一个非常直观的理解:假如我手上有一台相机,将现实中的树木、人物视为模型,渲染做的就是将这些模型投影到二维液晶显示屏上(或者更早一些的,胶片)。想查看与之相关的图形管道的更多信息,可参见Direct 3D
  • 动画:建模和渲染一般指的是静态的,换而言之,它们研究的是在时间轴上某一切片中的图形学。而动画研究的则是如何让图形“动”起来。大家如果玩过在纸上画图,通过快速翻页让图形动起来的游戏,动画与前二者的区别应该会很好理解:建模和渲染都是在一张纸上,而动画要的是连续多张纸翻页的效果。

一些需要用的计算机图形学的领域也列在下面。

  • 人机交互。举个例子,有些朋友可能觉得微软 Windows 界面不好看,麦金塔的界面就很好看(免责声明:我一直用的 Windows),为什么呢?他们可能会说某个图标应该圆滑一些,某种颜色应该调成另一种颜色。这就是人与机器之间的一种交互。
  • 虚拟现实。视觉信息获取与显示课(之后我会简称为“视觉信息课”)上的一个例子就很好:一个人戴着 3D 眼镜看可能会晕,但看全息图像就不会晕。为什么呢?因为二者传输的光学信息量不同,信息方式也不同。迁移到 VR 设备中,我们是否有更好的传递图形的方式,来让使用者获得更“真实”、更绚丽、更舒适的体验?
  • 可视化。渲染与动画大抵都可列入这一范畴。做数据统计时的三维统计图、云图等等,都属于可视化的一种。
  • 图像处理。最简单的图像处理就是平移、旋转、尺寸压缩了。稍微复杂的图像处理包括一些奇奇怪怪的映射,提取轮廓(只提取高频信息),滤波器处理等等。
  • 三维扫描。这一块主要应用建模:通过扫描仪获取物体表面信息,通过特定方式转化为三维模型。一些博物馆的应用推出的全方位观察文物功能中,文物的外形信息就可以通过此类方式导入到计算机中。
  • 计算成像。还是视觉信息课的一个例子:如果我们想获取大尺度、超高清图像(例如十亿像素成像),我们可以通过非规则相机阵列,像人眼一样拍摄若干张图,然后通过计算将其合成为一张图。计算成像通过引入包括计算机视觉、图像处理方法等等来拓宽成像的新方法。

至于有什么主要的应用——游戏、动画、特效、CAD/CAM、模拟、生物成像、信息可视化等等。(生物成像,比如显示细胞内部一些物质的移动)

1.2 图像设备

图像的输入/输出设备主要分为以下几种:

  • 输入设备,包括

    • 二维传感器(如数码相机)。相机内部常见的传感器包括 CCD 以及 CMOS 两种,通过色彩滤波阵列(或者马赛克我也不知道为啥叫这名字)来接受特定的红/绿/蓝光。
    • 一维传感器(如平板扫描仪)。同样采用阵列,但通过移动来实现二维的扫描。
  • 输出设备,主要分为显示设备和打印设备。

    显示设备的每个像素由红、绿、蓝光光源光强的调整来显示颜色。部分内容可以参考下文的“颜色视觉”。

    • Transmissive

      透射式,例如液晶 LCD 显示器。非偏振光首先经过第一层水平偏振片,经过液晶偏振后再经过第二层垂直偏振片,从而得到对应的偏振光。偏振旋转到 90°(fully open)时得到最大光强,0°(off)时光强为 0。

    • Emissive

      放射式。例如发光二极管 LED 显示器,通过施加电流可以实现不同强度的发光。

    打印设备

    • Binary

      “二进制”打印。例如喷墨打印机,每个像素只有“喷”与“不喷”两种状态。喷墨打印头在水平方向移动进行喷墨。图像分辨率由墨滴大小以及纸张前进距离决定。

    • Continuous Tone

      “连续彩印”。如染料热升华打印机,不同颜色的供墨纸经过线阵热敏打印机头热升华后印在对应的纸上,从而实现颜色的连续。图像分辨率由线阵打印机头的像素密度决定。

一张 \(h\times w\) 的光栅化图像对应 \([-0.5,h-0.5]\times[-0.5,w-0.5]\) 的二维空间,每个像素的中心都在整点上。像素的值即对应像素的颜色信息。这种信息由各种类型,例如 1-bit 灰度、8-bit RGB, 16-bit RGB, 16-bit “半精度”浮点 RGB, 32-bit 浮点 RGB。比较常见是 8-bit RGB,后面的内容主要也以这个展开。对应图像的存储也有各种方式,如 jpeg(有损压缩),tiff,ppm,png 等。(吐槽:ppm 存储最朴素但查看需要较专业的图像处理程序。)

了解更多

颜色模型有很多种,这里只考虑 RGB、CMY、HSV 颜色模型。

RGB 与 CMY 高度相似,区别在于 RGB 是加色(向黑色中加入颜色),CMY 是减色(从白光中滤去颜色)。

HSV 相较于面向硬件的前两者更面向用户,\(H\) 可看作一色盘,色盘上一颜色与其补色相差\(\pi\),另有两轴 \(V\)\(S\),分别表示亮度与色深。

Alpha 合成是将背景和图像进行合成的一种方法: \[ \textbf{c}=\alpha \textbf c_f+(1-\alpha)\textbf c_b \] 相当于将两张图像每个像素的颜色通过给定的 \(\alpha\) 混合起来。\(\alpha\) 的值一般存在另一个灰度图中,被称为 Alpha 遮罩(mask)或透明遮罩。

由于我们眼睛独特的处理颜色的方式,例如对于介于 1(白色) 与 0(黑色)之间的灰色,其对应的数值一般不会是 0.5,而是 \(1\times 0.5^\gamma\),其中 \(\gamma\) 是某个固定的,但因人而异的常数。可以通过放置两张图片,一张黑白像素交错,一张为对应的 \(0.5^\gamma\),通过肉眼比较来确定 \(\gamma\)

2 着色

2.1 简单光线追踪

思考一下眼睛如何“渲染”眼睛所在视平面某个“像素”。(不同颜色的)光线从光源出发,通过若干次反射/透射进入我们眼睛内的“传感器”(视网膜),根据光强混合得到最终的像素。

那么,假定我们现在已经有了一个充满各类物体的“世界”,如何让计算机“看”到这些物体,得到对应的图像呢?一个很朴素的方法是,模拟所有可能的光线从光源射出后的轨迹。由于可能的光线方向非常多,这个方法无法真正实现;但是利用采样的方法以及光路可逆的性质,我们可以从视平面“反着”射出(若干条)光线,采集光线对应的信息(如光强、RGB 颜色等等)。这就是光线“追踪”。

先不考虑光的反射、折射,我们可以设计一种朴素的光线追踪:首先从像素中心按特定方向射出一道光线,判断射线是否与物体相交。如果相交,根据物体表面的颜色/光源(如点光源、面光源等等)自身的若干信息,以及原点到相交点的距离计算实际像素应该渲染的颜色。(这个颜色与 \(\frac{1}{d^2}\) 成正比,其中 \(d\) 即原点到相交点的距离。具体见本节“了解更多”。)

那么,怎么去计算相交呢?实际应用中我们主要考虑射线与圆的相交,以及射线与三角形的相交两种情况。射线与圆的相交较为简单,可自行推导。与三角形的相交可以利用三角形的重心坐标 \[ p(\alpha,\beta,\gamma)=\alpha\vec a+\beta\vec b+\gamma\vec c \] \(\vec a,\vec b,\vec c\) 为三角形三顶点对应的坐标向量。重心坐标的一个良好性质是,当 \(\alpha+\beta+\gamma=1\) 时,点在三角形所在平面;当 \(\alpha,\beta,\gamma>0\) 时,\(p(\alpha,\beta,\gamma)\) 在三角形内。因而,我们只需计算对应的 \(\alpha,\beta,\gamma\),以及对应的光线 \(\vec e+t\vec d\)\(\vec e\) 为光线原点,\(\vec d\) 为光线方向)即可,此处略去推导过程。

  • 投影

想象一下我们实现上述光线追踪后得到的图像,我们会发现,它与我们在实际生活中看到的图像好像有些许差别。确切地说,我们得到的图像是平行投影(根据投影方向分为 orthographic 以及 oblique 两种),而我们实际眼睛看到的则遵循透视(perspective)原则,即“近大远小”。

(怎么去简单理解两种投影的区别呢?考虑我们图像所在的视平面。平行投影的焦点在无穷远处,对应的射入光线与平面垂直;而透视投影的焦点与平面有一固定距离 \(d\),射入焦点的平面根据所在位置与视平面呈不同的夹角。

2.2 照明模型

2.2.1 真实图像的生成

假设 \(I(x,y,t,\lambda)\) 为像源的空间辐射能量分布。一般来说 \(I=\frac{\int_S DKI_i(\vec N_0\cdot \vec L_0)dw_i}{\vec N_0\cdot\vec V_0}\),其中 \(I_i\) 是发光片元 \(dS_i\) (可视为环境中一点光源 \(R\))向曲片元 \(dS_J\) (可视为环境中一点 \(P\))辐射的光亮度,辐射立体角为 \(dw_i\)\(\vec N_0\)\(P\) 的单位法向量, \(\vec L_0\)\(P\) 指向 \(R\) 的单位向量(单位光线向量), \(\vec V_0\)\(P\) 指向观察点 \(U\) 的单位向量(单位视线向量);光通量分布函数 \(D\) 定义为向立体角内辐射的概率密度, \(K\) 是光通量的辐射比;\(S\) 为对 \(P\) 有光能贡献的所有发光表面集合, \(dS_i\)\(S\) 中的一片元。\(I,I_i,K\) 均是可见光波长 \(\lambda\) 的函数(光谱分布)。

光场瞬时光亮度定义为 \(Y(x,y,t)=\int_0^{\infty} I(x,y,t,\lambda)V_s(\lambda)d\lambda\),其中 \(V_s(\lambda)\) 代表相对光效函数。

2.2.2 简单光照明模型

光的传播

反射折射定律不予赘述。

能量关系。入射光强 \(I_i\) = 漫反射光强 \(I_d\) + 镜面反射光强 \(I_s\) + 透射光强 \(I_t\) + 被物体所吸收的光强 \(I_v\)

光的度量

立体角。片元 \(dS\) 向点光源 \(P\) 所张立体角 \(dw=\frac{dS}{r^2}\), 其中 \(r\) 为点光源到面源中心的垂直距离。

点发光强度。单位时间内通过 \(dS\) 的光能量记为光通量 \(dF\),而点光源在某个方向上的发光强度定义为该方向上单位立体角的内的光通量,即 \(I=\frac{dF}{dw}=\frac{dF}{dS}r^2\). 各向同性的点光源在各方向上的单位立体角内通过的光通量相等,即各方向发光强度相等。设发光强度为 \(I\),则点光源向外辐射的整个光通量为整个球立体角内的光通量,即 \(F=\int_{4\pi}Idw=4\pi I\).

Phong

简单光照明模型模拟物体表面对光的反射作用,只考虑直接光照的反射作用,而物体间的反射作用只用环境光同一表示。

  • 理想漫反射(Lambertian)

    漫反射光的空间分布是均匀的。记入射光强为 \(I_p\),物体表面点 \(P\) 法向量为 \(\vec N\),从点 \(P\) 指向光源的向量为 \(\vec L\),两者间夹角为 \(\theta\),则漫反射光强为 \(I_d=I_p K_d\cos\theta,\theta\in(0,\frac \pi 2)\)。其中 \(K_d\) 为与物体有关的漫反射系数, \(K_d\in (0,1)\)。(假如用反射度 \(R\) 来表示, \(K_d=\frac{R}{\pi}\)。)当 \(L,N\) 为单位向量时, \(I_d=I_p K_d(\vec L\cdot\vec N)\). 多光源情况下可表示为 \(I_d=K_d\sum\limits_{i} I_{p,i}(\vec L_i\cdot \vec N)\).

  • 镜面反射光(Blinn-Phong)

    镜面反射光强可表示为 \(I_s=I_pK_s\cos^n\alpha,\alpha\in(0,\frac \pi 2)\),其中 \(K_s\) 为与物体有关的镜面反射系数,\(\alpha\) 为视线方向 \(\vec V\) 与反射方向 \(\vec R\) 的夹角, \(n\) 为反射指数,反应物体表面的光泽程度,一般为 \([1,2000]\),数值越大物体表面越光滑。同样地,当 \(\vec V\)\(\vec R\) 为单位向量, \(I_s=I_p K_s(\vec R\cdot \vec V)^n\),其中 \(\vec R=2\vec N\cos\theta-\vec L=2\vec N(\vec N\cdot \vec L)-L\)。多光源情况 \(I_s=K_s\sum\limits_{i}I_{p,i} (\vec R_i\cdot \vec V)^n\)

  • 环境光(Ambient)

    环境光忽略光线方向。实际情况中通常用一常数模拟环境光,用公式表示为 \(I_e=I_a K_a\),其中 \(I_a\) 为环境光光强, \(K_a\) 为物体对环境光的反射系数。

  • Phong 光照明模型

    \[ I=I_aK_a+I_pK_d(\vec L\cdot\vec N)+I_pK_s(\vec R\cdot\vec V)^n \] 即:反射到视点的光强等于环境光光强+漫反射光强+镜面反射光光强。

    为减少计算量,可假设:

    • 光源在无穷远处,则 \(\vec L\) 为常数。
    • 视点在无穷远处,则 \(\vec V\) 为常数。
    • \((\vec H\cdot\vec N)\) 近似 \((\vec R\cdot\vec V)\),其中 \(\vec H=\frac{\vec L+\vec V}{|\vec L+\vec V|}\)

增量式光照明模型 在每一个多边形的顶点处计算合适的光照明强度或其他参数,然后在各多边形内部均匀插值,最后得到多边形光滑颜色分布。

双线性光强插值(Gouraud 明暗处理):计算多边形顶点平均法向,用 Phong 模型计算顶点平均光强,插值计算各离散边的各点光强,最后插值得到多边形内域中各点光强。

双线性法向插值(Phong 明暗处理):保留双线性插值,对多边形上的点和内域各点采用增量法;对顶点法向量进行插值,顶点法向量用相邻的多边形的法向量的平均值得到;由插值得到的法向量,计算每个像素的光亮度;假设光源与视点均在无穷远处。

2.2.3 局部光照明模型*

仅处理光源直接照射物体表面的光照明模型称为局部光照明模型,而可处理物体之间光照相互作用的模型称为整体光照明模型。

光的电磁理论略去不讲。微平面理论,假定微观角度物体表面粗糙不平,由许多微小的理想镜面平面组成,粗糙程度越高,漫反射系数越大。对一实际物体表面,其反射率为 \(DG\rho(\theta,\lambda)\),其中 \(D\) 为微平面方法向的分布函数,\(G\) 为由于微平面的相互遮挡或屏蔽而使光产生衰减的因子。

\(D\) 可通过 Gauss 分布函数 \(D=ke^{-\frac {a^2} {m^2}}\)\(\alpha\) 为微平面法向与平均法向夹角,\(m\) 为微平面斜率均方根)或 Berkmann 分布函数 \(D=\frac{1}{m^2\cos^4\alpha}e^{-\frac{tg^2\alpha}{m^2}}\)模拟。\(m\) 越小,表面越光滑。而对于 \(G\),若光路无遮挡或屏蔽,令 \(G=1\);对于部分反射光被屏蔽的情况,有 \(G_m=\frac{2(\vec N\cdot \vec H)(\vec N\cdot \vec V)}{\vec V\cdot\vec H}\), 对于部分入射光被遮挡的情况,有 \(G_s=\frac{2(\vec N\cdot \vec H)(\vec N\cdot \vec L)}{\vec V\cdot\vec H}\),其中 \(\vec N\) 为物体表面法向, \(\vec H\) 为微平面法向, \(\vec L\) 为入射光方向, \(\vec V\) 为观察方向。实际应用中取 \(G=\min\{1,G_m,G_s\}\)

\(R_{bd}\) 表示物体表面对入射自然光的反射率系数,写成反射光光强 \(I_r\) 与单位时间内单位面积上的入射光能量 \(E_i\) 的比,即 \(R_{bd}=\frac{I_r}{E_i}\)。式中 \(E_i\) 可用入射光光强 \(I_i\) 与单位面积向光源所张立体角 \(dw\) 表示为 \(E_i=I_i\cos\theta dw=I_i(\vec N\cdot\vec L)dw\),于是有 \(I_r=R_{bd}I_i(\vec N\cdot\vec L)dw\)。而反射率系数可写为漫反射率与镜面反射率的代数和,即 \(R_{bd}=K_dR_d+K_sR_s\),其中 \(K_d+K_b=1\),对应漫反射系数与镜面反射系数。 \(R_d=R_d(\lambda)\) 为物体表面漫反射率,受入射光波长影响;\(R_s=\frac{DG\rho(\theta,\lambda)}{\pi(\vec N\cdot\vec L)(\vec N\cdot\vec V)}\) 为物体表面镜面反射率。因而局部光照明模型最后表示为 \[ I_r=I_a K_a+I_i(\vec N\cdot\vec L)dw(K_dR_d+K_sR_s) \]

\(2^{[2]}=\{00,01,10,11\}\)

\(\mathscr F=\{00,10\}\)

$f_S $

\(\hat f_S:S\in \mathscr F\)

\(f'\sum\limits_{S\in \mathscr F} \hat f_S\chi_S(x)\)

function \(\mathbb E[(f-f')^2]\le \epsilon\) on \(\mathscr F\).

spectrum \(\sum\limits_{S\not \in \mathscr F} \hat f_S^2 \le \epsilon\) on \(\mathscr F\)

2.2.4 光透射模型*

透明效果的简单模拟。像素点颜色可通过 \(I=tI_b+(1-t)I_a\) 进行颜色调和(其中 \(t\) 为物体透明度),从而简单模拟出光的透射效果。

Whitted 光透射模型 在简单光照明模型上增加透射光,即得到 Whitted 光透射模型 \[ I=I_aK_a+I_pK_d(\vec L\cdot\vec N)+I_p K_s(\vec H\cdot\vec N)^n+I_tK_t' \] ​ ,其中 \(I_t\) 为折射方向入射光强度,\(K'_t\) 为透射系数。若透明体为一镜面反射体,则应加上环境反射光一项,得到 Whitted 整体光照模型 \[ I=I_aK_a+I_pK_d(\vec L\cdot\vec N)+I_p K_s(\vec H\cdot\vec N)^n+I_tK_t'+I_sK_s' \]

​ ,其中 \(I_s\) 为镜面反射方向入射光强度, \(K'_s\) 为镜面反射系数。

​ 实现细节。给定实现方向 \(\vec V\) 与法向方向 \(\vec N\),视线方向 \(\vec V\) 的反射方向 \(\vec S=2(\vec N\cdot\vec V)\vec N-\vec V\),而折射方向 \(\vec T=k_f(\vec N-\vec V')-\vec N\),其中 \(k_f=\frac{1}{\sqrt{\eta^2|\vec V'|^2-|\vec N-\vec V|^2}},\vec V'=\frac{\vec V}{|\vec N\cdot\vec V|},\eta=\frac{\eta_1}{\eta_2}=\frac{\sin\theta_1}{\sin\theta_2}\)\(\eta_1,\eta_2\) 分别为视点所在空间的介质折射率、物体的折射率。此时 \(\vec T\) 为非单位向量。或采用 \(\vec T=-\frac{1}{\eta}\vec V-(\cos\theta_2-\frac{1}{\eta}\cos\theta_1)\vec N\),其中 \(\cos\theta_2=\sqrt{1-\frac{1}{\eta^2}(1-\cos^2\theta_1)}\)\(\cos\theta_1=\vec N\cdot\vec V\)。此时 \(\vec T\) 为单位向量。

Hall 光透射模型 Hall 光透射模型可模拟透射高光的效果。对于理想漫透射面,透射光光强在各个方向均相等。此时点 \(P\) 处漫透射光光强为 \(I_{dt}=I_pK_{dt}(-\vec N\cdot\vec L)\),其中 \(K_{dt}\in [0,1]\) 为物体漫透射系数,\(\vec L\) 为光源方向, \(\vec N\) 为面法向。对透射高光现象, \(I_t=I_pK_t(\vec T\cdot\vec V)^n\),其中 \(I_t\) 为规则透射光在视线方向的强度, \(I_p\) 为点光源强度, \(K_t\) 为物体透明系数, \(n\) 为反应物体表面光泽的常数。简化处理同 Phong 光照明模型;区别是用 \(\vec H_{t}\cdot\vec N\) 代替 \(\vec T\cdot\vec V\), 其中 \(\vec H_t\) 可视为一虚拟理想透射面法向,有 \(\vec H_t=\frac{\vec L+\frac{\vec V}{\eta}}{\frac{1}{\eta}\cos\theta_1-\cos\theta_2}\),简化并单位化有 \(\vec H_t=sign(\eta_1-\eta_2)\frac{\eta_2\vec L+\eta_1\vec V}{|\eta_2\vec L+\eta_1\vec V|}\),其中 \(\eta_1>\eta_2\Rightarrow \theta_1<\theta_2\)\(sign\) 取正号,否则取负号。

简单光反射透射模型

综合简单光照明模型,Whitted 光透射模型和 Hall 光透射模型, \[ I=I_aK_a+\sum\limits_{i}I_{p,i}(K_{ds}(L_i\cdot N)+K_s(\vec H_{s,i}\cdot \vec N)^{n_i})+\sum\limits_{j}I_{p,j}[K_{dt}(-\vec N\cdot L_j)+K_t(\vec N\cdot\vec H_{t,j})^{n_t}]+I_tK_t'+I_sK_s' \]

2.2.5 整体光照明模型*

光线追踪算法 从视点进行视线跟踪,跟踪镜面反射与折射。算法终止当且仅当 1)光线未碰到任何物体 2)光线碰到了背景 3)光线经多次反射与折射后对于视点的光强贡献衰减到很小 4)光线反射或折射次数大于给定值。

算法加速。自适应深度控制;包围盒及层次结构;三维 DDA 算法;空间八叉树剖分技术。

辐射度方法 略。当时没学会,饶了我吧。

2.2.6 实时真实感图形学技术*

基于图像的绘制技术。通过视图插值还原六维全光函数,从而实现加速绘制。

景物模拟。将物理原理引入图形学中,如布料模拟采用质点-弹簧模型。

3 图形流水线

3.1 观察

假如将我们丢到计算机里一个包含若干物体的三维“世界”空间,我们需要什么样的信息去“看”这个世界,或者说,怎么去获得图像呢?事实上,最简单的模型只需要我们相对于世界坐标的位置以及我们看的角度(即视角),共五维。更复杂的模型还需要考虑其他的信息,例如,我们的眼睛可以看作是一台带凸透镜的相机,这个凸透镜的焦距、视屏距离透镜中心的距离,都可能是我们需要考虑的因素。

有了这些信息,我们又应当如何去将物体投影到我们的视平面呢?图形学一般采用视图变换来将视野内的物体投影到屏幕空间,然后做进一步地处理。视图变换将对象由对象空间转换到普遍的世界空间,再通过镜头变换转换到相机空间(以相机为原点的坐标轴),经过投影变换转换到 Canonical View Volumn(或 NDC,归一化设备坐标),最后通过视口变换转换到屏幕空间。

要想实现变换,我们需要变换矩阵这一基本工具来帮助我们在世界空间内进行坐标的转化。这一部分的内容见“变换矩阵”一节。上述的投影变换、视口变换以及镜头转换,详见“视图变换”一节。由世界空间向相机空间的转换,详见“相机模型”一节。

3.1.1 变换矩阵

  • 2D 变换

    2D 变换矩阵主要包括

    • 缩放(Scaling):\(\begin{bmatrix}a&0\\0&b\end{bmatrix}\)

    • 错切(Shearing)

      沿 \(x\) 方向\(\begin{bmatrix}1&s\\0&1\end{bmatrix}\),沿 \(y\) 方向 \(\begin{bmatrix}1&0\\s&1\end{bmatrix}\)。(\(s\) 可以看作是错切距离,也可看作是错切角度 \(s=\tan \phi\)

    • 旋转(Rotation):\(\begin{bmatrix}\cos\phi&-\sin\phi\\\sin\phi&\cos\phi\end{bmatrix}\)

    • 镜像(Reflection):

      \(x\) 轴对称 \(\begin{bmatrix}1&0\\0&-1\end{bmatrix}\)\(y\) 轴对称 \(\begin{bmatrix}-1&0\\0&1\end{bmatrix}\)

      变换的合成遵循右先原则,而分解一般采用 SVD 分解(一般分解出 3 个:旋转,缩放,旋转)。逆运算可以应用矩阵的性质(例如对角矩阵取倒数,正交矩阵取转置)。

  • 3D 变换

    3D 变换与 2D 变换类似,大部分情况可以转化为固定某一坐标轴做变换。一些需要补充的情况如下:

    • 绕给定方向 \(\textbf{w}\) 旋转:先求旋转矩阵 \(\textbf{R}\),将 \(z\) 轴旋转到 \(\textbf{w}\) 方向,绕方向旋转后再把 \(z\) 轴转回来。可以通过限定特征值 \(\lambda=1\) 来确定唯一的旋转矩阵。

    • 变换法线:对表面施加 \(\textbf{M}\) 变换后,法线旋转矩阵为 \(\textbf{N}=(\textbf M^{-1})^\top\)。证明略。

    • 平移变换:有时我们需要移动坐标。我们只需要多加一维代表“常数 \(1\)”即可。2D、3D 平移变换如下。 \[ \begin{bmatrix}1&0&x_t\\0&1&y_t\\0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\1\end{bmatrix}\quad \begin{bmatrix}1&0&0&x_t\\0&1&0&y_t\\0&0&1&z_t\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix} \]

    • 仿射变换:将平移、缩放、旋转混合起来。任何一个矩阵都可以用仿射变换表示,并分解成平移、缩放、旋转矩阵。 \[ \begin{bmatrix}1&0&0&x_t\\0&1&0&y_t\\0&0&1&z_t\\0&0&0&1\end{bmatrix}\begin{bmatrix}a_{11}&a_{12}&a_{13}&0\\a_{21}&a_{22}&a_{23}&0\\a_{31}&a_{32}&a_{33}&0\\0&0&0&1\end{bmatrix} \] 不同坐标系之间的转换可以通过仿射变换实现。

3.1.2 视图变换

正如本章序所述,

图形学一般采用视图变换来将视野内的物体投影到屏幕空间,然后进行进一步地处理。视图变换将对象由对象空间转换到普遍的世界空间,再通过镜头变换转换到相机空间(以相机为原点的坐标轴),经过投影变换转换到 Canonical View Volumn(或 NDC,归一化设备坐标),最后通过视口变换转换到屏幕空间。

视口变换

在 OpenGL 中,NDC 范围一般是 \([-1,1]^3\) 的正方体,而对于我们的屏幕空间,若分辨率为 \(n_x\times n_y\),屏幕空间的范围即为 \([-0.5,n_x-0.5]\times [-0.5,n_y-0.5]\)(像素中央为整点)。对应的变换矩阵容易写出。(注意 \(z\) 轴的深度信息保持不变,用于后续做消隐等工作) \[ \begin{bmatrix}x_{screen}\\y_{screen}\\z_{screen}\\1\end{bmatrix}=\begin{bmatrix}\frac{n_x}{2} & 0&0&\frac{n_x-1}{2}\\0&\frac{n_y}{2}&0&\frac{n_y-1}{2}\\0&0&1&0\\0&0&0&1 \end{bmatrix}\begin{bmatrix}x_{ndc}\\y_{ndc}\\z_{ndc}\\1\end{bmatrix} \] 投影变换

  • 正投影变换

    我们先考虑最简单的投影变换:正投影变换。

    假定我们的视野空间(视屏所能看到的空间)为 \([l,r]\times[b,t]\times[f,n]\)。那么变换矩阵为 \[ \textbf{M}_{orth}=\begin{bmatrix}\frac 2 {r-l} &0&0&-\frac{r+l}{r-l}\\0&\frac 2 {t-b}&0&-\frac{t+b}{t-b}\\0&0&\frac 2 {n-f}&-\frac{n+f}{n-f}\\0&0&0&1\end{bmatrix} \]

  • 投影变换

    假如我们想实现透视的“近大远小”,如下图,点 \((1,0)\) 变换到 \((3,0)\),点 \((1,1)\) 变换到 \((1,3)\)

    一种实现这种效果的方法是,离的近的坐标除以一个小的分母,离得远的坐标除以一个大的分母。以我们现有的定义还无法做到,但是仍然有办法:只需要把第四维常数 \(1\) 改为常数 \(w\) 即可: \[ \begin{bmatrix}x\\y\\z\\1\end{bmatrix}\rightarrow\begin{bmatrix}x'\\y'\\z'\\w\end{bmatrix}(x_{fact}=\frac{x'}{w},etc.) \]

    透视变换的变换矩阵如下。(注:由于采用右手坐标系,\(z\) 轴正方向指向垂直视屏向内,\(n,f\) 均为负数。我们也可以引入 \(\theta\)(相机到视平面的角度)来表示 \(n,f\)\[ \textbf{P}=\begin{bmatrix}n&0&0&0\\0&n&0&0\\0&0&n+f&-fn\\0&0&1&0\end{bmatrix}\Rightarrow \textbf{P}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\sim\begin{bmatrix}\frac {nx}{z}\\\frac{ny}{z}\\n+f-\frac{fn}{z}\\1\end{bmatrix} \]

    img

    因而采用透视的投影变换对应的变换矩阵为 \(\textbf{M}_{orth}\textbf{P}\)。(注意,各坐标要除以 \(w\) 才是正确的 NDC)

镜头转换

假定镜头空间转换到世界空间对应的变换矩阵为 \(\textbf{M}_{cam}\)(类似于物体空间向世界空间的转化)。那么镜头转换的变换矩阵即为 \(\textbf{M}_{cam}^{-1}\)

3.2 图形流水线

按对象顺序绘制的图形流水线主要分为以下四个步骤:

[应用层] 命令流 -> 顶点处理 -> 变换几何 -> 光栅化 -> 片元 -> 片元处理 -> 混合 -> [输出] 帧缓冲图像

图像渲染管线 API 包括

  • 硬件管线 API,如 OpenGL, Direct3D 等。这些多用于游戏渲染等实时渲染。
  • 软件管线 API,如 RenderMan。主要用于高质量动画,耗时长,渲染质量好。

3.1 剔除

图形流水线一般是 Object-order Rendering,换而言之,对每个对象进行处理。考虑到视野空间内只有若干对象可见,我们需要考虑一些比较有效的方式快速剔除掉不在视野内的对象。

  • 视景体剔除(View Volume Culling)

    将若干对象打包在一起做一次快速检测。例如若干对象的位置都在某个球内,只需要简单衡量球与视野空间是否有交,即可进行快速剔除。

  • 闭合剔除(Occlusion Culling)

    对象包在其他对象之内无法被看见。

  • 背面剔除(Backface Culling)

    对于那些“背对”我们视平面法向量的对象(点积简单判断),即可快速剔除。

3.2 顶点处理

顶点处理实质就是准备光栅化所需要的对象顶点的若干信息。通过应用若干矩阵变换(见前文“照相模型”一节),顶点在屏幕空间的坐标可以由世界空间坐标求得。其他包括顶点的颜色、表面法线、对应材质/纹理的坐标等信息,也可通过一定方法获得。

注:注意在透视中表面法向量不能简单通过变换矩阵做变换,还需要进行修正。

3.3 光栅化

众所周知,我们在屏幕平面看到的图片是由一个个像素组成的,像素有大小,因而看到的线其实是一个个像素(矩形)组成,而并非一条精确的线。因此需要考虑如何填充像素。这就是光栅化需要做的事情。

下文的光栅化主要考虑线段和三角形的光栅化。多边形可以简单规约到三角形的处理。

裁剪

我们的屏幕大小并不是无限大,有时我们会遇到图形的一部分处于屏幕之外。这时我们就需要进行裁剪。一般裁剪会选择在进行几何转换后进行。

  • 直线段裁剪

    朴素:利用平面法线等信息判断两个端点是否在平面两端,然后朴素求解交点。

    Cohen-Sutherland:对主窗口边界直线所划分出的九个方块编码,处理直线时先求两端点编码,之后利用位运算判断是否部分/完全丢弃该线段。如果部分丢弃,则求出线段与窗口某边的交点,一分为二,对两条再进行考虑。

    中点分割:如果部分丢弃,则取中点一分为二,对两条线再进行考虑。只有加法和除二运算,非常适合。

    梁友栋-Barskey(据说更快):将裁剪问题转化为不等式条件,形如 \(x_1\le x+u\Delta x\le x_2,y_1\le y+u\Delta y\le y_2\),然后求式子中的 \(u\),从而确定裁剪坐标。

  • 三角形裁剪

    实际裁剪一般采用 BSP 树程序进行(我还没看,这一部分先跳过)。用直线段裁剪的方法类似处理即可。

  • 多边形裁剪

    Sutherland-Hodgman。考虑多边形是由有序点集/有向边表示的,从起点开始取点,每次考虑下一条边是否部分/完全可见,并返回对应的端点,从而得到新的有序点集。

扫描转换

  • 直线段的扫描转换算法(画线算法)

    考虑一条给定端点的直线段进行像素填充。为方便进行绘图,我们要求直线斜率 \(|k|\le 1\)。 若不然,则将 \(x,y\) 位置调换。

    • 中点画线法

      考虑在 \(x_p+1\) 处的情况,假定 \(y_p < f(x_p+1) < y_p+1\)。如果 \(f(x_p+1)\) 不大于区间中点,取 \(y_p\),否则取 \(y_p+1\)

      实际用的更多是 \(F(x,y)\),只需求 \(F(x_p+1, y_p+0.5)\),若不为正则取 \(y_p\),否则取 \(y_p+1\)

      为省去乘法运算,可使用步进。为省去小数运算,整体可以乘一定值从而消去分母。

    • Bresenham

      其实就是上面 \(f(x)\) 的改良,同样使用步进,然后扣掉0.5,从而变成只用判断正负即可确认下一坐标取值。

    画线可能遇到的走样问题:可以将原先的单个 pixel 模糊成若干个 pixels,片元增多。

  • 三角形的扫描转换算法

    三角形各边的转换可以采用画线算法。对于三角形内部,可以根据顶点设定 \(x\)\(y\) 的范围,然后计算重心坐标是否满足要求。(对于三角形片元的着色,可以使用重心坐标以及顶点颜色进行简单的线性插值。)

了解更多

  • 圆弧的扫描转换算法

    圆具有良好的对称性,一般绘制边界时只考虑 \(\frac 1 8\) 圆弧,剩下的圆弧可以通过对称方式模仿填充。只考虑1/8圆弧也有一个好处,取斜率绝对值显然不大于1的部分,这样我们可以模仿直线段的中点画线法画圆。可以采用顶端右侧 \(\frac 1 8\) 圆弧,每次取点考虑 \(y_p\)\(y_p-1\)

  • 多边形的扫描转换与区域填充

    • 多边形的扫描转换

      扫描线算法:利用所谓活性边表(其实就是通过排序确认哪条边会先被扫描到),只考虑当前在集合内的边与扫描线的交。同时注意正确的交点取舍(例如边界与扫描线重合计为1,或端点与扫描线重合计为0)。

      边界标志算法:对多边形边界经过的扫描线上的像素打标记,真则填充,假则不填。不需维护边表及排序,比扫描线更适合硬件实现。

    • 区域填充算法

      递归算法。非常简单,不赘述。

      扫描线算法。从栈中取出种子店,先左右横着填,然后对刚刚填过的每个像素检查上下相邻的像素,确认是否需要填充;如果要,就将该段空白区间的最右像素压入栈中。

3.4 片元处理与混合

片元处理阶段主要是计算每个片元的颜色和深度,而混合阶段则对片元进行混合,得到对应像素。最常见的方式是选择深度最小的片元的颜色(消隐)。

消隐

在光栅化阶段我们获得了若干片元的信息,如颜色,深度等。考虑到片元之间存在遮挡关系,我们要将应当被隐藏的片元消去不显示。

  • 消除隐藏线

平面对直线段的遮挡判断算法。先包围盒简单判断。求直线与相应无穷平面的交,若有交,一段不遮挡一段部分或完全遮挡。求投影与平面边界投影的所有焦点,对于切割出的所有线段,取第一段中点,若中点在投影内则第一段被遮挡,其他交替不被/被遮挡。

多平面情况可以用栈解决。

  • 消除隐藏面

Z 缓冲区算法。补充一深度缓冲器,只有当待绘制像素深度值大于缓冲器中对应深度值时才更新帧缓冲器。为节省空间,可逐个处理帧缓冲器上的每个像素。为了加速处理可以对深度做一个简单的离散化(\(2^b\) 切割 \([0,1]\) 区间,可以均匀切割,也可以在处理透视情况时采用非线性切割)。处理点与多边形的包含性检测可采用射线法、弧长法(稳定但弧长难求,可通过建立四象限,查看象限变化并对每种情况赋弧长变化)。扫描线 Z-Buffer,区间扫描线法略去。

区域子分割算法。将窗口不断四分割,直到情况足够简单为止,根据实际情况绘图。

光线投射算法。从窗口像素位置做射线,与物体求交。实际仍然是 Z-Buffer。

着色

  • 逐顶点着色(Gouraud Shading)

    在顶点处理阶段计算各顶点对应的颜色,然后光栅化阶段根据顶点颜色做插值。缺点是几何片元不够多、不够小时着色细节不足。

  • 按片元着色(Phong Shading)

    在光栅化阶段插值每个顶点的法向量、位置,在片元处理阶段再计算每个片元的颜色。具有更优秀的着色细节。

着色还需要考虑着色频率的问题。着色频率反应像素之间颜色变化的快慢,着色频率越大,需要的细节程度也就越高,计算精度也要求越高。(比如高光的着色频率应当显著高于漫反射的频率。)

纹理映射

我们想给对象加上一些特殊的“皮肤”,比如圆木凹凸不平的树皮。这时可以添加纹理,进行纹理映射。这一部分的内容见“图像处理”下纹理映射一节。

4 图像处理

4.1 信号处理

4.1.1 引入

假定我们需要将某个连续函数存储在计算机中,需要计算某个位置的值。这个函数也许是非代数的(不存在解析解),也许解析解非常复杂,总之,计算非常麻烦。是否有一种替代方法呢?有!我们可以适当地对函数进行采样(在若干个不同的位置上计算函数的值),并且根据这些样本重构这个函数(插值)。一个明显的事实是:重构往往不能真正的还原这个函数。我们采集样本的数量,或者采样频率,很大程度上会影响我们重构函数与原函数之间的偏差(失真)。怎么去尽可能的降低失真,正是接下来我们需要研究的问题。

信号处理的一大应用在于“打电话”(一维信号处理)。我们的手机/座机需要将周围的声音转化为特定的数字信号,之后再用这些信号尽可能地还原出原本的声音。考虑到波动,数字信号并不能连续地采集声音,因而只能离散地进行采样。同时,我们也不可能无限制的提高采样频率。那么,有没有什么办法实现反失真呢?

我们可以思考一下,在特定采样频率下,什么时候会出现失真。简单地思考,在两次采样之间,如果信号发生了较大的波动(即出现高频信号),那么就很容易造成失真。低通滤波器正是用于过滤高频噪声的滤波电路。

4.1.2 卷积

低通滤波器是如何在保留尽可能多的细节的同时裁剪高频信号的呢?这就要提到卷积——将两个函数“杂糅”组合成一个函数的方法,可以用于数据的平滑处理。连续函数之间、离散函数之间的卷积分别为 \[ (f*g)(t)=\int_{-\infty}^{\infty} f(p)g(t-p)dp\\ (f*g)[t]=\sum\limits_{i}f[i]g[t-i] \] 想简单了解卷积,可参考 3B1B 的相关视频,在此不再赘述。可以简单验证卷积具有交换律、结合律与分配律。为了由数字信号重建模拟信号,我们定义连续函数与离散函数之间的卷积 \[ (f*g)(x)=\sum\limits_{i}f(x-i)g[i] \] 二维的卷积也很好定义: \[ (f*g)(x,y)=\int\int f(x',y')g(x-x',y-y')dx'dy'\\ (f*g)[x,y]=\sum\limits_i\sum\limits_j f[i,j]g[x-i,y-j] \] 更高维度的卷积以此类推。

4.1.3 滤波器

假定我们现在想处理模拟信号 \(f\),而 \(g\) 则是我们的(卷积)滤波器。为了不影响信号,\(g\) 的和一般为 \(1\)。一些常见的卷积滤波器包括:

  • 盒状滤波器 \(B\)(Box Filter) \[ B(x)=\begin{cases}\frac 1 {2r} &-r\le x\le r,\\0&otherwise\end{cases}\\ B[x]=\begin{cases}\frac 1 {2r+1} &-r\le x\le r,\\0&otherwise\end{cases} \]

  • 帐篷滤波器 \(T\) (Tent Filter) \[ T(x)=\begin{cases}1-|x|&|x|\le 1,\\0&otherwise\end{cases} \]

  • 冲激滤波器 \(\delta\)(Dirac Impulse / Dirac Delta Filter) \[ \int_{-\infty}^{\infty}\delta(x)f(x)dx=f(0) \] 简单理解:\(\delta(x)\)\(x=0\) 处为 \(+\infty\),但和依然为 \(1\)。这个实际没啥用,等同于什么也没滤掉。

  • 高斯滤波器 \(G\)(Gaussian Filter) \[ G_{\sigma}(x)=\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{x^2}{2\sigma^2}} \] \(\sigma\) 即标准差。考虑到高斯滤波器收敛到 \(0\) 的速度较快,可以直接截取成离散形式。

  • B 样条滤波器 \(BS\) (B-Spline Cubic Filter) \[ BS(x)=\frac 1 6\begin{cases}-3(1-|x|)^3+3(1-|x|)^2+3(1-|x|)+1&|x|\le 1,\\(2-|x|)^3&1\le |x|\le 2,\\0&otherwise\end{cases} \] 一种简洁的记法: \(BS=B*B*B*B\).

  • 卡特穆尔-罗姆立方过滤器 \(C\)(Catmull-Rom Cubic Filter) \[ C(x)=\frac 1 2\begin{cases}-3(1-|x|)^3+4(1-|x|)^2+(1-|x|)&|x|\le 1,\\(2-|x|)^3-(2-|x|)^2&1\le |x|\le 2,\\0&otherwise\end{cases} \]

  • 米切尔-内特拉瓦利立方过滤器 \(M\)(Mitchell-Netravali Cubic Filter) \[ M(x)=\frac 1 3BS(x)+\frac 2 3 C(x) \]

要想将上述一维卷积滤波器应用到二维,只需要简单的 \[ f'(x,y)=f(x)f(y) \] 容易证明和依然为 \(1\)。这种由低维滤波器组合得到的高维滤波器一般称为可分离滤波器。

滤波器还有一些需要考虑的性质:

  • 纹波

    无纹波(Ripple Free)指滤波器在整数范围内和为 \(1\),而有纹波则指和不为 \(1\)。除高斯滤波器,上述的滤波器均为无纹波滤波器。

  • 连续性

    • 盒状滤波器不连续;
    • \(C^0\):帐篷滤波器;
    • \(C^2\):B 样条滤波器;
    • etc.

4.1.4 采样理论

如果你看过一些通过若干正弦波叠加,无限逼近某个函数(信号):

也许你会产生这样的疑问:这些正弦波是怎么确定的?傅里叶变换 \(\mathcal F\) 正是解决这一问题的利器:傅里叶变换实现了信号“时域” \(f\) 与“频域” \(\hat f\) 之间的转换。假如你有一个时域上的信号 \(f\),希望由若干个频率为 \(w_i\) 的正弦波表示(如果你知道傅里叶级数……),傅里叶变换可以告诉你这些正弦波对应的幅值 \(\hat f(w_i)\);同样的,如果你知道频域上的 \(\hat f\),可以通过逆变换来还原时域上的信号 \(f\)\[ \hat f(w)=\int_{-\infty}^{\infty} f(t)e^{-iwt}dt\\ f(t)=\frac 1 {2\pi}\int_{-\infty}^{\infty}\hat f(w)e^{iwt}dw \] 傅里叶变换有一些美妙的性质:

  • 平方的积分相同。简单理解:信号的能量在时域上和频域上都应当是相等的。 \[ \int (f(t))^2dt=\int(\hat f(w))^2dw \]

  • 时域与频域上的伸缩反向。 \[ \mathcal F\{f(\frac x b)\}=b\hat f(bx) \]

  • 时域的平均值即为频域中 \(0\) 位置的幅值。

最为美妙的性质与卷积相关:两函数时域卷积的傅里叶变换即为频域乘积,频域卷积即为时域乘积的傅里叶变换。 \[ \mathcal F(f*g)=\hat f\hat g\quad \hat f*\hat g=\mathcal F(fg) \] 于是我们的卷积处理可以考虑在频域上解决。上文中提及的部分(一维)滤波器在频域中的表示如下: \[ \mathcal F\{B\}=\frac{\sin \pi u}{\pi u}\\ \mathcal F\{T\}=\frac{\sin^2 \pi u}{(\pi u)^2}\\ \mathcal F\{BS\}=\frac{\sin^4 \pi u}{(\pi u)^4}\\ \mathcal F\{G_\sigma\}=\sqrt {2\pi}G_{\sigma}(2\pi u)=\frac{1}{\sigma}e^{-\frac{(2\pi u)^2}{2\sigma^2}} \] 这是否也算是高斯滤波器的一个独特性质。

接下来我们回到走样(失真)的问题。我们此前采用的采样方法是:每间隔 \(T\) 时间在时域上对信号 \(f\) 进行采样,得到若干采样脉冲 \(fs_T\),其中 \[ s_T(t)=\sum\limits_{i}\delta(i-T) \] 经过傅里叶变换后,我们得到的是一系列几乎无差别的,形状相同的函数图像: \[ \mathcal F\{fs_T\}(u)=(\hat f*\hat s_T)(u)=(\hat f*s_{\frac 1 T})(u)=\sum\limits_i \hat f(u-\frac i T) \] 因而,在不使用滤波器的情况下,当采样间隔 \(T\) 过大(采样率不足),频域图像高频段会出现重叠的部分(高次混叠波),进而产生走样4141。因而在采样时,我们有如下方法实现反走样:

  • 增加采样率,从而减少高频信号重叠区域大小。
  • 采样前使用低通滤波器,去除高频信息。4142

利用样本重建信号时,也可以选用适合的滤波器来实现反走样,在保留基波的同时降低高次混叠波。重采样由于带宽(频谱宽度)减少,也可以降低混叠。

4.1.5 图像信号

图像信号是一种二维信号,同一维信号类似,我们也可以用(离散)滤波器对信号进行处理,从而实现我们想要的图像处理效果。例如:

  • 图像模糊:让图像中的“轮廓”变得更加平滑,对应保留低频信息,去除高频信息。可以采用高斯滤波器做高斯模糊。

  • 图像锐化:让图像中的“轮廓”变得更加突出,对应保留高频信息,去除低频信息。可以让原图减去高斯模糊。

  • 阴影投射:给图像加阴影。平移原图像后进行模糊。

对图像进行采样也会遇到走样的问题,这种走样一般表现为锯齿和摩尔纹。如上文所述,我们可以先对原图进行适当模糊,再进行采样。

如果要改变图像分辨率/采样频率,我们就需要对图像信号进行重采样。重采样一般分为重构与采样两个步骤。利用卷积,我们可以将重构与采样时应用的滤波器组合起来,得到重采样滤波器4151

对图像信号做滤波时会遇到如下的问题:

  • 图像边缘的处理。比较好的方法是在接近边缘时选择不同大小的滤波器。
  • 滤波器(半径)大小。一般来说,滤波器越大,处理得到的图像越模糊。

4.2 纹理映射

4.2.1 引入

如何在计算机里存一个有着各种复杂花纹的花瓶呢?一种简单的方法是:将花瓶外观的细节记录在一张图上,再将图映射到我们的模型表面。这张图就是纹理图,而映射即是我们接下来将要研究的纹理映射。

纹理可以简单地做个划分:

  • 二维纹理域:将纹理变换到三维物体表面形成最终图像。

  • 三维纹理域:各点对应的纹理由三维纹理函数唯一确定。

  • 几何纹理:对物体表面每个点沿法向量方向位移若干单位长度,从而实现粗糙外观。

  • etc.

假定我们现在已经有了纹理图,做纹理映射不可避免地要遇到两个问题:如何映射,以及如何解决走样问题(例如相机离物体很远时产生的锯齿)。

纹理查找问题。

4.2.2 映射函数

4.2.3 反走样

TAT 施工部分

之后应该会在这里加一些有关纹理查找问题的 Notes,参考 Games 101。

5 几何表示

5.1 引入与基础

5.1.1 显示表示

5.1.2 隐式表示

5.2 曲线

5.2.1 贝塞尔曲线

5.2.2 B 样条曲线*

5.3 平面

5.3.1 贝塞尔平面

5.4 三维表示

F 脚注


  1. 4141.上文提到,信息的采样频率过低会造成走样。要正确采样(能够从采样后的信息重建出原始信号),采样频率必须大于原始信号最大频率的2倍,这通常被称为采样定理,采样频率称为 Nyquist limit 或 Nyquist rate。这个定理使用了"最大频率"术语,这就意味着这个信号必须是有带宽限制(band-limited)的,没有任何频率能超过这个限制。换句话说,在相邻采样的间隔中,信号必须足够平滑。 ↩︎
  2. 4142.按此描述,盒状滤波器的处理效果似乎是最好的:它在完全剔除了高频信息的同时保存了低频信息。然而,它的实用性并不强:一是频域函数难用,还存在着负数部分,二是实际效果并不如想象的那般出色。相较而言,帐篷滤波器、高斯滤波器更实用,效果也较为出色。 ↩︎
  3. 4151.实际应用中,重采样时可以先对行进行重采样,然后再对列进行重采样,从而实现“空间换时间”的效率提升。 ↩︎