透视(Perspective)是绘画中最重要的基础之一。透视本质上是几何学的内容,目的是利用人眼成像原理来在一个二维的画布上“模拟”出一个三维场景来。除去诸如抽象、写意之类的风格,以及逐渐兴起的利用 VR 直接进行真 3D 场景绘画等情况之外,都很难绕开透视这个话题。

如果对具体的几何细节不感兴趣的话,透视其实总结起来就是“近大远小”四个字,最多再加上一些简单的诸如“地平线”、“消失点”等数个基本概念,经过(大量)写生练习就能从经验上掌握,并能随心所欲地对想要描绘的场景进行 realistic 的刻画,甚至创造出一些华丽的超广角镜头等效果。例如下面这张(未经授权擅自贴上来的)Paul Heaston 的作品,他很喜欢画这种广角 sketch,更多作品请看他的 Instagram

但是在我自己的学习过程中却发现透视看似简单易懂,但是真正实际应用的时候却困难无比,不知道是自己的领悟能力太差还是自己做的“大量”写生练习还不够“大量”。所以我想从几何的角度来稍微深究一下透视的细节,看看能不能得到一些信息帮助自己更快地掌握透视的 intuition。毕竟最终 intuition 还是最重要的,实际绘画过程中如果要依靠尺规甚至是代数计算的话,那就和 3D renderer 无异了,毕竟我们不是工程制图,并不需要追求绝对精确。

首先简单介绍一下学习透视可以参考的几本书。第一本是 Ernest R. Norling 的《Perspective Made Easy》,这是一本比较古老(1939 年第一版)的小册子,短小精干,基本上包含了所有你需要了解的绘画透视相关的内容——或者更确切地说,覆盖了所有其他讲绘画透视的书里会讲到的内容。进阶书可以参考 Scott Robertson 和 Thomas Bertling 的《How To Draw: Drawing And Sketching Objects And Environments From Your Imagination》,这本书非常详细地介绍了各种形状(例如圆锥、拱门以及复合结构)的透视画法,以及阴影形状的确定等等,全都有详细地通过辅助线来得到精确的位置和比例的方法。我觉得这样的作画方法有点过头了,不过这本书里主要是画科幻风格的飞船、战车之类的,这比普通场景绘画对结构和比例的精确度要求更高一些也算是情有可原啦。我比较喜欢这本书的前面部分对 Cone of Vision 的介绍,虽然只有一点点内容,但也属于其他同类书籍里不太有提到的我觉得有助于理解透视的 intuition。此外还有一本韩国人 Dongho Kim 写的《Space Drawing: Perspective (Volume 1)》我也比较喜欢,主要内容跟其他同类书其实差不多,但是它里面特别强调了在画透视的时候比起“平行线要收敛到消失点”,“相同大小的面会随着距离迅速变窄”更为重要。我觉得这是我自己练习中体会最深的一点,也是本文想要详细探讨的主要话题,具体等下再讲。最后还有 Marcos Mateu-Mestre 和 Jeffery Katzenberg 的《Framed Ink: Drawing and Composition for Visual Storytellers》以及同系列的 Framed Perspective 等几本书,它们的重点是从分镜、戏剧性、情绪渲染、场景刻画等方面来介绍透视的应用,插图好看并且很有视觉张力,可以作为掌握里透视基础之后的应用参考。

下面从实际例子来探讨一下实际绘画中会碰到的问题,由于篇幅限制这里主要集中在单点透视(one point perspective)上,也就是只有一个消失点的情况。现在大部分 digital painting 的程序都提供透视辅助线的功能,例如下面是 Procreate 里的单点透视辅助线功能,只要选定消失点,就能自动生成收束到消失点的辅助线,需要的时候还能让你画的线和辅助线对齐,非常方便。

不过这里至少有两个问题需要事先搞清楚:消失点在图中的什么位置?这么多辅助的发散射线中如何选取“主要”的两条参考线——例如这两条线代表街道或者走廊的边界,假设左右对称的情况下我们需要确定上图中 𝜽 角的大小。

第一个问题不难:消失点在地平线(horizon)上,而地平线和视平线(eye line)是一样的,所以如果你是俯视(例如站在楼顶)则消失点在图中的位置较高,如果你趴在地上或者模拟蚂蚁视角,则消失点较低(可以参考下面的一些图作为例子)。第二个问题看起来好像也很简单,我在下面贴了一些从 Unsplash 上找来的照片,可以看到在实际中这个角度可以选取的范围很大,从宽阔的街道到狭窄的小巷都能表达。看照片似乎也比较直观,宽的场景用大角度。但似乎也不全是,例如第二行第二张和第三张图都是街道,但是一个用的角度比另一个大很多。实际在构图的时候非常犯难。

要搞清楚这个问题,似乎得要做大量类似的练习,逐渐培养经验和“感觉”,一个比较可行的方式似乎是从摄影的角度出发,对于一些常见的场景(街道、走廊、房间等)拍摄广角、普通和长焦镜头下的照片,进行对照,熟悉几种常见的组合情形下的构图效果,也许就能应对大部分情况了。

当然这是自己构图时需要解决的问题。如果是写生或者临摹,那一般已经有一个参考图了,这个时候根据参考图来确定消失点的位置和主要的参考线就会容易很多。例如下面这张走廊的照片,地面和墙壁相交的线提供了很直观的参考线。

根据上面的参考,我在 Procreate 里设置好了辅助线,画了如下的草图。乍一看好像收束到消失点的线的角度也大差不差,但是似乎感觉上哪里不太对,草图中的走廊好像看起来更宽一些,柱子看起来好像更矮更胖一些。

这是为什么呢?当然潜在的问题可能有很多,不过这里有一个主要的问题就是:即使消失点找对了,收束的线的角度也差不多,但是纵深的长度“缩小”的速度没有掌握好的话,就会看起来很奇怪。从某种程度上来说这甚至比保证所有平行线收束到同一个消失点更重要。纵深长度这里可以用柱子背光的一面来衡量。如果仔细观察会发现虽然我的草图中柱子暗面(在 2D 画布中的)宽度有随着深度在逐渐变小,但是照片中变小的速度要快很多,特别是在远处几乎都已经看不见柱子的暗面,而全部变成了亮面。

如下图所示,我用蓝线测量了第一个柱子的“宽度”,然后将其复制为等长的红色和绿色线短,可以看到第二个柱子的暗面的宽度连第一个柱子的一半都不到,而再往远处两三个柱子,同样的宽度可以覆盖超过十个柱子(我已经数不过来了)。

我发现这是我在画有透视的图的时候最容易犯的错误。即使在最近深切认识到它的重要性之后,以及在有具体的参考图的时候,也很难画到准确,因为人的视觉系统是被设计来从二维图中提炼三维信息的,所以看到近大远小的参考(无论是二维的参考图还是三维实际场景在我们视网膜上的投影)时会自动做(一定程度的)矫正,让我们不太能注意到物体(在投影面上的像)在逐渐缩小,即使知道在缩小,也由于这种自动矫正让我们经常很难估计清楚具体的缩小速率。在有参照的时候似乎最靠谱的办法就是闭上一只眼睛伸直手臂直接用笔头之类的参照物来做测量和比较,反正我几乎每次测量都会被惊讶到。

如果是自己构图,其实有比较简单的辅助线方法可以“复制”线段,这个在许多讲透视的书里都会讲,可能会有一些其他方法的变种,不过下面这种方法似乎简易性和灵活性都不错。已知下图中的地平线,以及地平线上的消失点 V,并已知从 A 到消失点的直线上的一个线段 AB(例如上面例子中第一个柱子的暗面),如果在三维世界中有和他相临的同样长的线短,在图中应该是多长呢?下面给一个 JSXGraph 示例:

如上图所示,我们想要找到 C 点在直线上的位置,为此我们从 A 点作与地平线平行的辅助线,并在上面随便取一点 B2,连接 B2 到 B 并延长,与地平线相交于 V2,V2 即是所有与 B-B2 平行的直线的消失点。由于辅助线与地平线平行,所以其上的线段比例与真实三维世界中的比例是一样的,所以我们可以直接取 C2 点,使得 A-B2 和 B2-C2 的距离相等,再连接 C2-V2,它与 V-A 的交点即我们要找的 C 点。因为 A、B、C 分别是 V2-A、V2-B2 和 V2-C2 这三条等距的平行线与 V-A 的交点。图中红色的点是可以拖拽的,可以看到 B2 的选取是任意的,并不会改变 C 的位置。

类似的方法不仅可以等长复制,还能画出在三维世界中任意长度比例的线段在画面中的投影,只要在辅助线中作出对应的比例即可。类似的,我们还能做出多份复制,如下图所示,我们作出了线段 AB 的四份相临拷贝:

这个辅助线方法虽然很实用,操作起来也不太复杂,但总归是比较麻烦的。如果有一些经验公式能给出比例缩小的速度之类的,在画的时候大概有个谱,而不需要特地作辅助线的话会方便很多。在上图中我们直接测量并打印出来了每两个相临线段在画面中投影长度的比例。首先拖动 B2 点的位置是不改变这些比例的,因为 B2 本身是无关的辅助点,但是有趣的是拖动 A 点的位置这些比例也不变,并且保持 3/1、4/2、5/3、……、(k+2)/k 的比例。

这难道就是我们想要的经验公式?这似乎有些不合常理,特别是拖动 A 点改变 V-A 线与垂直线之间的夹角时,就好像是我们在改变相机的镜头(从长焦变到广角),如果在这样的情况下比例都是保持不变的话,那画画会变得轻松愉快很多,只是我们平时观察到的情况似乎并不是这样的。

难道是我们的作图方法有问题吗?不完全是,在上面的示例中有一个 bug:我们允许用户随意拖动 A 点的位置,但是不论 A 点怎么动,AB (在二维画布中)的长度始终是固定的,这是导致我们图中比例不变的原因。而这个长度固定实际上是不合情理的:假设我们的二维图像是对一个固定的三维场景进行拍摄的到的,那么改变 A 点的位置就需要通过改变相机的位置和参数来实现,而在做出这样的改变的时候 AB 在二维画布中的长度也会随之变化。

而这也是我在学习透视时最大的疑问:虽然我们有方法可以在纵深上复制一个线段,但是最初的线段应该是多长呢?例如地面上有一个正方形,假设 A 点是该正方形的一角,现在我确定了消失点 V 和它到 A 的连线,但是我要如何确定 B 点的位置,也就是 AB 在二维画布中的长度呢?

我发现这个问题在所有绘画主题的透视书里都被直接忽略了。但这似乎是导致我自己在做透视构图时画出来的结构看起来非常奇怪的主要原因之一。仔细想了一下,也许在绘画透视书中对这个问题避而不谈的原因是这会牵涉到具体的投影成像过程——并不是所投影成像是个多复杂的过程,但是要的到这个长度就会需要知道具体三维世界中的各种参数,包括相机和成像平面的位置,A 和 B 在三维世界中的具体坐标等等。这相当于让二维画师在构图的时候先做一个三维场景建模:虽然确实有人这么做,而且这种思考方式有时候对构图非常有帮助,但对大部分情况来说都有点本末倒置了。

那么在实际绘画构图中我们究竟要如何来大致确定这个长度呢?这个我至今没有搞明白。也许还是得通过熟悉各种常见场景和镜头焦距组合下的构图,然后凭经验来画。写本文的一个目的其实是想通过搞清楚其背后的对应关系,看有没有一个经验公式之类的,通过脑海里一个非常粗略的三维布局能得出一个大致长度范围来。结果是我并没有找到简单的经验公式:因为最终结果依赖于一大堆参数(例如 A 点的高度、深度,相机和成像面的位置等等)。

对于如何确定 AB 的长度依然有兴趣的同学,可以继续看下面的内容。要确定这个,Procreate 里提供的那个消失点和射线的透视工具就不好用了,因为我们需要确定三维世界中的具体配置。 具体的相机成像原理非常复杂,但是我们忽略具体镜头组的实现,可以将其抽象为一个点加一个成像平面。如下图所示。此外我们还需要一个三维空间中的坐标系和一个二维成像平面中的坐标系,具体选取无关紧要,为了简化坐标运算,我们可以把相机中心放在三维坐标系原点,让成像平面与 Z 轴垂直,并取 Z 轴和成像平面的交点(图中的 principal point)为成像平面中的二维坐标原点。成像的过程其实就是已知一个点 X 的三维坐标,求相机中心 C 到 X 点的连线与成像平面的交点 x 在二维成像坐标系下的坐标。

求解方法可以是纯几何的,也可以通过坐标计算的到。几何方法通常在工程制图等相关的课程中会学到,下面继续用 JSXGraph 来做一个示例。这里我们已知三维空间中的一个立方体,为了简单起见我们假设立方体是紧贴着 Picture Plane 的,这里我们可以控制的是相机的位置,包括横向的 shift,垂直的 height 和纵向的 depth,在下图中可以通过下方的三个滑杆来控制。

下图看起来有点复杂,它其实是两个 view 混合在一起的:首先上方的 Picture Plane 到 Camera 的部分表示了一个 top view,也就是三维场景从顶端往下看的结果,调节相机的 shift 和 depth 可以看到 Camera 这个点在移动,但是由于是 top view,调节 height 不会有什么影响。然后下面从 Horizon Line 到 Ground Line 的部分则是 front view,表示从正面看的样子,这里的 Horizon Line 的高度也就是相机的高度,而黄色的面 ABCD 则是这个立方体的正面。我们要通过辅助线画出紫色的顶面 BCFE 在 front view 中的样子,当然下图中已经画出来了,具体画法如下:

首先从 Camera 的位置作垂直直线与 Horizon Line 相交,这个点即 front view 中所有与成像平面垂直的直线的消失点。下面以 E 点为例,在 front view 中连接 B 与消失点 VP,E 点在 front view 中的投影 E2 将位于这条直线上的某个位置。现在在 top view 中作 E 到 Camera 的连线,与 Picture Plane 相交于一点,从该点作垂直直线并与 B-VP 相交,该交点即为我们要找的 E2 点。类似地,我们可以作出 F 在 front view 中的投影 F2 点,进而得到立方体的顶面的投影成像。具体这样作图的原理就不在这里解释了,说起来比较费劲,但其实对着图想一想还是蛮直观容易明白的。

我在图中显示出了 BC 线段和 F2-C 线段的长度对比,这个值就是我们之前想要搞清楚的,一个正方形(这里立方体的顶面)的两边在一点透视下两条边的比例。这里我们得到了具体的数值,但是这个数值不止依赖于相机的三维坐标参数,如果立方体本身的位置变化,也还会继续变化。所以似乎并不能得出一个非常简单的经验规则用于帮助绘画构图。

此外,我还在图中标出了 F2-C 的中点 M,并算出了 CM 和 M-F2 两个线段的比例,这也是我们刚才试图计算的“第一个线段”和“第二个等长线段”在纵深上的比例。可以看到现在这个比值在相机参数变化是不再是一个固定的值。

尤其需要注意的是:在调节相机的 depth 参数的时候,可以看到 VP-C 和 VP-B 这两条线是不动的,所以说像 Procreate 那样用一个消失点加射线来做透视辅助的时候,即使确定了具体的射线(本文第二张图中的 𝜽 角),也还有一个自由参数未定。这个自由参数导致纵向单位线段的长度可以是(一定范围内的)任意值,这个值取得不好会让图看起来不是自己原本想要的样子,在没有使用测量和辅助线构图时,还会出现的一个问题时这个参数在图的不同的地方不一致,会让整个图的结构直接崩塌。

如何解决这个问题呢?我也不知道……🥲 从辅助工具的角度来说,也许相比于 Procreate 那种消失点加射线的 tool,Krita 里这样给某一个具体的平面标示正方形网格的工具会更实用一些,特别是对于初期透视构图还不完全熟练的情况来说。

作为最后的挣扎,我们可以尝试用直接建立坐标系来做一下计算,看看能否得出一个非常简单的公式来作为我们构图时的 intuition。事实上投影成像的数学原理属于射影几何的范畴,使用里面的齐次坐标 (Homogeneous Coordinates) 等数学工具来表达,很多东西会变得简洁明了。如果对射影几何本身没有那么大的兴趣,也可以参考一些以成像与投影的应用为主题的书,例如计算机图形学和一些计算机视觉(特别是 multi-view 视觉相关)的书里通常都会讲到。我自己随便找了一本叫《Multiple View Geometry in Computer Vision》的书里翻看了一些相关的内容。不过我们这里要做的计算其实相当简单,直接用普通三维向量加简单的解析几何也不会太复杂,就不专门引入齐次坐标那些东西了。

假设三维坐标系如下图所示。不过我们现在并不要求相机被固定在坐标原点,记相机的位置为 CC,不过方便起见我们让成像平面固定在 Z 轴距离 ff 处,所以当相机离开坐标原点时,ff 就不再是相机的成像焦距了。现在假设空间中有一点 AA,它的像 A~\tilde{A} 可以通过连接 AACC 的直线与成像平面的交点得到。

该直线上的点可以由 C+t(AC)C+t(A-C) 来表示,其中 tRt\in\mathbb{R} 是一个标量参数。令 Z 轴坐标等于 ff,即可得到 A~\tilde{A} 点对应的 tA=(fC3)/(A3C3)t_A=(f-C_3)/(A_3-C_3)。这里 A1,A2,A3A_1,A_2,A_3 分别表示 AR3A\in\mathbb{R}^3 的 X、Y、Z 三个坐标值。

现在我们假设 A 与 X 轴方向单位长度 uu 处有一相临点 BB,Z 轴方向同样长度处有 DD 点,换句话说,B=(A1+u,A2,A3)B=(A_1+u,A_2,A_3)D=(A1,A2,A3+u)D=(A_1,A_2,A_3+u)。现在我们来求 A~B~/A~D~\|\tilde{A}-\tilde{B}\| / \|\tilde{A}-\tilde{D}\| 这个比值。首先,很容易求得 tB=tAt_B=t_A,从而得到 A~B~=tA(u,0,0)\tilde{A}-\tilde{B}=t_A(u,0,0),该线段的长度也就是 tAut_Au。注意到如果 A 点紧贴着成像平面(亦即 A3=fA_3=f),此时 tA=1t_A=1,这个长度和三维世界中原本的长度 uu 完全相等,这和我们的 intuition 是一致的。类似我们可以求得 tD=(fC3)/(A3+uC3)t_D=(f-C_3)/(A_3+u-C_3),所以

D~=(C1+tD(A1C1),C2+tD(A2C2),f) \tilde{D} = \left( C_1 + t_D (A_1 - C_1), C_2 + t_D (A_2 - C_2), f \right)

不过现在 A~D~\|\tilde{A}-\tilde{D}\| 的值是一个算术上很复杂的表达式,两个值的比虽然可以带入相关参数从数值上算出来,但是无法得出任何简单的 intuition 可供参考。(摊手)看来真的并没有什么简单的规则可循,还是老老实实多做写生练习吧,不过反过来想,如果世界的透视结构能够用简单的规则概括出来的话,也许构图创造的乐趣也会减少许多呢。

不过直接计算也并不是毫无用处,可以帮助我们理解那些通常在绘画透视教程里直接给出的概念背后的原因。例如上面的例子中,如果我们让 DD 点沿着 ADAD 方向延伸到无限远,也就是令 uu\rightarrow \infty 会怎样?根据上面 D~\tilde{D} 的坐标公式,很容易得到此时 D~(C1,C2,f)\tilde{D}\rightarrow (C_1, C_2, f)。这正是从相机出发与 Z 轴平行的直线和成像平面的交点。注意到这个极限坐标与 DD 点本身的初始位置无关(但是与方向有关),它是所有同 Z 轴平行的直线共同的消失点。

现在假设我们考虑从 AA 出发沿着 ADADABAB 中线方向前进的直线,通过同样的方法可以得到这个方向上的平行线的消失点为 (C1+fC3,C2,f)(C_1+f-C_3, C_2, f)。注意到这个和刚才的消失点的 Y 轴坐标是一样的,只是 X 轴坐标平移了。虽然大部分透视教程中会讲到单点透视、两点透视和三点透视,但是事实上任何一组平行线都会投影出一个不一样的消失点。特别地,所有与地面平行的平行线组(包括我们刚才考虑的两组)的消失点全部集中在一条直线 (,C2,f)(\star, C_2, f) 上,这条直线也就是所谓的 Horizon Line。

Horizon Line 有时也被称为 Eye Level 或者其他类似的名字,因为它在我们人眼所在的高度:注意刚才我们算出来的 Y 轴坐标 C2C_2 也就是相机(人眼)位置的 Y 轴坐标。当然这里隐含假设我们的视线是沿着 Z 轴方向的,假设我们抬头或者低头看,地平线在图像上的位置会相应地变低或者变高(通过刚才类似的计算会得到消失点的 Y 坐标不再是 C2C_2)。另外地平面也没有什么特殊的,如果场景中有存在其他和地面坡度不一样的平面,它们会形成另一个不同的“horizon line”,这在旧金山那种大陡坡的街道下面很容易看到。

两个不同坡度的平面形成了两个不同的 vanishing line。旧金山街道,图片来自 Unsplash @reza565

最后,刚才提到了仰头和低头主要的效果是改变了地平线在画面中的高度,假设我们保持视线的方向不变,但是转动成像平面的角度,就会形成移轴摄影 (Tilt-shift Photography) 的效果。移轴会对投影图像带来很大的改变,例如在合适的角度下原本收敛到一个消失点的平行线会恢复“平行”效果,感兴趣的同学可以自己算一下。由于这样的特性,移轴摄影经常被用来拍摄建筑,这样即使拍高楼也可以保证原本平行的线在画面中也是平行的,而不是收束到一个消失点。

移轴摄影的场景看起来像玩具模型特摄的效果。图片来自 Unsplash @centelm

但是人眼应该是不能像相机那样不改变注视方向只改变成像平面角度的,所以人的视觉系统会被这样“矫正”后的图片搞晕,导致这些图片看起来很像是玩具模型拍出来的特摄照片。其中一个原因就是对于像高楼之类的大场景,在这个尺度下透视的近大远小以及平行线收束的效果在正常情况下会非常明显,在移轴矫正过之后反而看起来很不自然了,但是如果这只是像玩具大小一般的模型的话,就很合理了,所以人眼会觉得这样的照片看起来不像真实场景而像玩具。直接在网上搜索“移轴摄影”可以找到很多例子,还蛮好玩的。