卡通渲染及其相關技術

前言

卡通渲染是圖形學中一個有趣的話題,屬於非真實感計算機圖形學(NPR)的範疇,在NPR領域中也最多地被應用到實際遊戲中,近年來流行的《守望先鋒》,《英雄聯盟》,《DOTA2》,《崩壞3》等遊戲中都或多或少地出現過卡通渲染的身影,恰好最近對這個領域的內容作了一些了解和探索,所以就對其中涉及的一些經典技術做一個概述。

卡通渲染的分類

在具體討論技術手段之前,先就卡通渲染做一個分類。卡通渲染最關鍵的特徵包括不同於真實感渲染的藝術化光影效果描邊。以這兩個關鍵的特徵為卡通渲染分類的話,可以將近年來遊戲中常用的卡通渲染分為美式卡通風格日式卡通風格。美式卡通風格在色彩上比較連續,有漸變色,著色風格很大程度上依賴於藝術家定義的色調(tone),而在陰影和高光方面常常採取誇張和變形的做法,比較典型的是《軍團要塞2》;日式卡通風格往往角色造型更寫實,但在著色方面,則趨向於大片大片純色色塊,並有的明暗交界,例如《崩壞3》。雖然這樣的分類並沒有清晰界限,但易於描述,接下來我們就按照美式卡通和日式卡通的分類,從光影和描邊兩個維度上分別列舉各類技術實現。

軍團要塞2的卡通渲染,人物造型誇張,但著色連續,接近真實感光照

崩壞3遊戲截圖,著色以單色色塊為主,有明顯的明暗交界

描邊

描邊是一個比較常用的技術,在《Real Time Rendering》中有相當篇幅的綜述,大致來說包含了三類:

(1)基於視角的勾邊,這部分的計算依賴於我們的一個直覺觀察:當我們的視線和某個表面相切時,這個表面上的像素點往往就是模型的邊緣,基於這個觀察,我們可以用dot(viewDir, normal)^{k} 來估計一個像素的「邊緣程度」,當然,這個值也可以用來作為紋理坐標去採樣一張預定義的「輪廓紋理」

基於視角的描邊,最大的缺點是線寬粗細差別較大,不易控制

(2)基於幾何生成方法的描邊,這類方法的特點是描邊本身是一個單獨的幾何體,通過特殊的方法繪製出來,比較常見的做法是shell method,原理和實現都比較簡單:首先在繪製結束正常的模型後,將需要描邊的物體改用正面剔除再繪製一遍,在VS中將頂點沿著法線方向膨脹一定距離,然後在FS中將模型用純色輸出。另外一種叫做z-bias的方法,也是繪製背面,但不膨脹,而是把背面頂點的Z值稍微向前偏移一點點,使得背面的些許部分顯示出來形成描邊效果。

基於shell method的繪製方法,實現簡單,線寬較為均勻

(3)基於圖像處理的描邊,這類方法的實現可以說更接近於「邊緣」這一概念的本質定義,什麼是「邊緣」呢?邊緣就是在深度或者法線上不連續的位置。因此為了獲取邊緣,我們只需要在圖片上找到深度或者法線不連續的位置即可,因此,我們需要將深度信息和法線信息以貼圖的形式傳入,運用邊緣檢測演算法去尋找這些像素。這類方法的優點是描邊的線寬一致,缺點是需要額外的法線和深度信息,當然,由於近年來流行的延遲渲染框架,法線和深度本來就是G-Buffer的一部分,因此往往不需要額外繪製法線和深度的信息。

基於邊緣檢測的描邊方法,分別用深度信息和法線信息進行單獨的邊緣檢測,而後合併起來成為最終的描邊

美式卡通中的做法

美式卡通中往往傾向於使用基於圖像處理的描邊方法來生成均勻一致的描邊效果。在《英雄聯盟》[1]中小兵和英雄的勾邊效果就是用Sobel運算元對深度信息進行邊緣檢測來獲得的。由於遊戲中只需要針對小兵和英雄勾邊而不需要對場景地圖進行勾邊,因此在LOL中,勾邊的計算並非全屏後處理,而是逐物體進行的,這樣的好處是可以隨意控制哪些物體描邊,每個物體可以單獨指定描邊顏色,缺點是當物體較多時(尤其是skinned mesh較多時)計算量會增大。一個折衷的方案是,在進行正常繪製的階段用stencil buffer標記出需要描邊的物體,然後用一個全屏的後處理,對stencil buffer標記的像素進行邊緣檢測,當然這樣的話,就很難給每個物體單獨指定描邊顏色了。

實際上,在LOL中有兩種類型的描邊,一種是小兵和英雄的固定描邊,另一種是防禦塔發出攻擊警報或者某個單位被點選時才產生的紅色描邊,這兩種描邊在處理上略有差別,前者直接使用邊緣檢測的結果作為最終描邊,而後者則是對邊緣檢測結果再進行一次模糊,藉此來擴大和柔化描邊效果。

LOL中兩種類型的描邊,可以看出第二類描邊的線寬更寬,並且有明顯的過度效果

日式卡通的做法

日式卡通中往往傾向於使用基於幾何體生成的方法去描邊,這類描邊方法相較於另兩類方法的好處在於線寬更容易為美術所控制,而在日式卡通中,往往需要粗細有變化的描邊去體現角色不同部位的特徵,例如在《GUILTY GEAR Xrd》[2][3]中,角色的描邊就是通過幾何體生成的方法,結合了shell method和z-bias method,並引入了逐物體的頂點色來控制描邊細節,同時也是為了保證描邊粗細不會隨著攝像機視距發生變化,具體來說,頂點色存儲的信息包括:

  • R通道:控制toon shading的閾值,和描邊無關,和著色有關,這個我們後面描述
  • G通道:控制頂點根據視距膨脹的強度(這個部分具體操作我也沒有完全弄清楚,希望了解的朋友來補充)
  • B通道:控制描邊的z-bias,越大則描邊越不可見
  • A通道:控制描邊的粗細

上述做法中比較直觀的理解是,通過引入逐頂點的線寬係數,使得整個描邊的細節更易為美術控制,但是從我的理解來看,線寬控制只需要一個值即可,視距無關的粗細可以通過給偏移值offset.xy乘以當前頂點的z值來實現,似乎並不需要三個值來控制。

沒有vertex color,輪廓線寬沒有粗細變化

有vertex color, 輪廓線可以按照美術的需要去設定逐頂點粗細變化

著色

Cel Shading和Tone Based Shading

先來描述兩種經典的NPR著色方法,分別是Cel Shading[4]和Tone Based Shading[5]。

Cel Shading的基本思想是把色彩從多色階降到低色階,減少色階的豐富程度,從而實現類似手工著色的效果,具體來說,可以用如下計算方法:

celCoord = dot(normal, lightDir)

I = tex(paletteTex,  celCoord).rgb * lightColor.rgb * k_{d}

其中,Kd表示模型自身的貼圖顏色,celCoord表示法線和光照方向的點積,用作一維色彩表的查找坐標,而paletteTex則是由美術繪製的一維色階表,一般來說是由幾個純色色塊組成的,如下圖:

上述做法可以用於模擬卡通渲染的漫反射分量,卻並沒有考慮到視角相關的光照分量的模擬,因此很難實現類似菲涅爾效果的卡通渲染。實際上,也可以用類似的查找表的思路來視角相關光照分量的色階離散化[6],只需要將一維查找表擴展到二維即可:

celCoord = vec2(dot(normal, lightDir), dot(normal, viewDir)^{r} )I = tex(paletteTex,  celCoord).rgb * lightColor.rgb * k_{d}

相應地,查找坐標也擴展到了二維。

不同於Cel Shading,Tone Based Shading的風格化是基於美術指定的色調插值,並且插值得到的色階是連續的。首先需要由美術指定冷色調和暖色調,而最終模型的著色將根據法線和光照方向的夾角,在這兩個色調的基礎上進行插值,具體演算法如下:

I = frac{1 + dot(normal, lightDir)}{2} k_{cool} +(1-frac{1 + dot(normal, lightDir)}{2}) k_{warm} k_{cool} =k_{blue} + alpha k_{d}k_{warm} =k_{yellow} + eta k_{d}

其中,Kd仍是模型自身色彩貼圖,Kblue,Kyellow和alpha,beta則均是自定義的參數。

基於tone based shading繪製的球體

日式卡通的著色

前面已經描述過,日式卡通在著色方面比較典型的特點是以大量純色為主,進一步說,往往只有「明暗」或者「冷暖」兩個色階,因此光照計算往往最後也要映射到離散的色彩表上。 仍然以《GUILTY GEAR Xrd》為例,它也一定程度上包含了Cel Shading和Tone Based Shading的部分思想,將色階離散成為「明暗」兩個色調,並由美術指定冷暖色調的顏色:

I= ((darkness < threshold) ? k_{cool} * k_{SSS} : k_{warm}) * lightColor.rgb * k_{d}darkness = dot(normal, lightDir) * AO

上述公式表示了這個卡通渲染的漫反射部分,其中threshold表示明暗交界的閾值,在遊戲中通過頂點色的R通道來實現逐頂點的控制。Kcool和Kwarm由美術逐物體地指定Ksss是對模型次表面散射效果的模擬,對皮膚而言一般呈粉紅色,通過美術繪製的SSS貼圖來實現逐像素控制,並且只有暗部的像素才會受SSS貼圖的影響。Kd是模型自身的顏色貼圖。darkness表示了某個像素的明暗程度,用於確定色調的冷暖。除了正常的dot(normal, lightDir)項,遊戲中還加入了由美術繪製的AO貼圖,來實現一些邊角縫隙的暗部效果。我在實現時又引入了動態的陰影部分,最終darkness的計算公式為:

darkness = dot(normal, lightDir) * AO * shadow

其中shadow是由shadowMap的演算法計算得來的。

高光的計算更簡單一些:

I_{spec}=(spec < threshold_{spec} )? vec3(0.0):specColorspec = specMask * dot(normal, halfVec)^{specPower}

其中,spec表示高光的強度,threshold可以由美術逐物體或逐頂點指定,specMask和specPower由美術繪製的貼圖來逐像素控制,類似於phong著色中的specular和glossiness的作用。specColor可以由美術逐物體地指定,也可以把AO,shadow和明暗色調作為影響因素添加進去。最終的著色結果將漫反射和高光疊加即可。

在實際遊戲中使用時,上述方法往往還需要配合美術針對具體模型進行法線修正。 根據模型頂點位置和拓撲關係計算出的法線往往細節過度,表現在上述卡通渲染的結果上就是往往會出現許多不需要的暗部細節,修正的方法是使用模型法線轉印,給精細的模型一個近似的低精度proxy(比如用一個球形代表模型的頭部,用一個圓柱形代表模型的胳膊或者腿),然後用proxy上附近頂點的法線作為模型的法線來使用。此外,還需要考慮到明暗交界處反走樣的問題,這裡不做展開。

根據我的觀察和研究,《崩壞3》應該是沿用了《GUILTY GEAR Xrd》中的卡通著色方法和美術工藝,因此在效果上和後者非常相似。

基於不同的冷暖色調設定值得到的卡通渲染結果

美式卡通的著色

Valve在其遊戲《軍團要塞2》[7]中描述了他們的卡通渲染方案,這個卡通渲染演算法也在後來影響了《DOTA2》的卡通渲染的實現。他們將卡通渲染著色分為了view dependent termview independent term。兩部分的計算分別如下:

I=I_{viewIndependent} + I_{viewDependent}

I_{viewIndependent}=k_{d} [a(ar{n}) + sum_{i=1}^{L}{c_{i}w((alpha }  (ar{n}cdot ar{l})+eta)^{r})]

I_{viewDependent}=sum_{i=1}^{L}{[c_{i}k_{s}max(f_{s}(ar{v}cdotar{r_{i}})^{k_{spec}},f_{r}k_{r}(ar{v}cdotar{r_{i}})^{k_{rim}})]} +(ar{n}cdot ar{u})f_{r}k_{r}a(ar{v})

這部分的實現在其他知乎專欄文章[8]中有詳細的描述和實現,這裡不再做詳細的解釋。直觀地來說,在視角無關的照明部分,《軍團要塞2》中除了考慮了一般的漫反射部分外,還加入了基於模型法線方向的環境光分量,此外,通常的漫反射分量改為了wrapped diffuse;而在視角相關的照明部分,《軍團要塞2》除了考慮了一般的鏡面反射外,還基於菲涅爾現象實現了類似邊緣光的效果。實際上,類似ambient cubewarpped diffuse的做法也被Valve應用在《Half Life》等其他遊戲中[9],在早期3D遊戲中用以模擬全局照明。雖然這些方法都是一些純粹的trick,但是能夠以很小的開銷實現不錯的效果。

《軍團要塞2》的最終著色結果,可以看出明暗交界處有明顯的泛紅(warpped diffuse的效果), 模型邊緣可以看到邊緣光

經典的half-lambert方法也算是warpped diffuse的一種變體

風格化高光和陰影

在[7]的Future Work里,還提到了可變形狀的高光[10]和風格化陰影[11],這兩個風格化渲染演算法的思路都比較有趣,這裡簡單就其實現原理進行一個概述。

可變形狀的高光

我們在日式卡通渲染的著色部分描述了一個相對較為簡單的高光計算方式,從計算方法可以看出,該方法和經典的Blin-Phong模型有很多相似之處,尤其是對高光強度的計算上,都採用了這個計算項:

specMask * dot(normal, halfVec)^{specPower}

這個halfVec也就是我們常說的半形向量,計算方法是:

ar{H} = frac{ar{L} + ar{V}}{left| ar{L} + ar{V}
ight|}

其中,L和V分別是光源方向和視線方向。

從我們上面描述的卡通渲染高光演算法可以看出來,改變卡通渲染高光形狀的關鍵就在於改變這個半形向量。因此文章中就針對半形向量定義了一系列的修改操作,這些修改操作可以疊加使用,也可以單獨使用,每個操作對高光形狀的影響均不同,具體有以下幾個操作:

(1)平移,改變高光的位置:

ar{H} = normalize(ar{H} + alphaar{du} + etaar{dv})

這裡,du和dv表示的是切線空間中的x軸和y軸,也就是切線和副法線,alpha和beta是自定義平移參數,最終偏移後的向量需要進行歸一化處理。

(2)有方向的縮放,沿著切線空間的某個軸縮放高光形狀:

ar{H} = normalize(ar{H} - sigma(ar{H} cdot ar{du})ar{du})

sigma是自定義參數,範圍是(0, 1],上式將使高光沿著切線空間的X軸縮放。

(3)分割,將一塊連續的高光切分成兩塊:

ar{H} = normalize(ar{H} - gamma_{1}sgn(ar{H}cdotar{du})ar{du} - gamma_{2}sgn(ar{H}cdotar{dv})ar{dv})

其中,sgn是符號函數,負數返回-1,否則返回1,gamma1和gamma2分別是自定義參數,若其中一個為0,則只沿著另一個方向將高光切為兩部分,若兩個參數均不為0,則高光被切成四塊。

(4)方塊化,將趨於圓形的高光變成方形:

	heta = min(cos^{-1}(ar{H}cdotar{du}), cos^{-1}(ar{H}cdotar{dv}))

sqrnorm = sin(2	heta)^{n}

ar{H} = normalize(ar{H} - sigma*sqrnorm*((ar{H}cdotar{du})ar{du}+(ar{H}cdotar{dv})ar{dv}))

其中,n是自定義整數,n越大高光形狀越方,sigma則定義了方形高光區域的大小,範圍是[0, 1]。

上述四個操作的具體實現可參見這篇文章[12]。

四個基本的操作符

風格化陰影

類似於風格化的高光,風格化的陰影也是在標準的陰影計算流程之後,定義了一系列針對標準陰影的操作,通過這些操作,配合用戶自定義的參數,便可以達到風格化陰影的效果,總的來說,共有四類操作:

(1)膨脹/腐蝕(Inflation):擴大或者縮小陰影範圍,用參數i來控制

(2)亮度(Brightness):陰影區域的亮度,可以用於模擬半影區的效果,用參數b控制

(3)柔度(Softness):陰影邊界處的柔和程度,用參數s控制

(4)抽象度(Abstraction):陰影形狀的抽象程度,用參數alpha控制

幾種操作和相應的效果

整個風格化陰影的生成是基於圖像空間的從一個已經生成的精確陰影圖開始。可以分成五個階段:

(1)精確陰影的生成,由於是基於圖像空間的,因此對精確陰影圖的生成方法沒有特別要求,可以是shadow volume,shadow map,ray tracing或者其他陰影生成技術,但必須要注意的是這裡的陰影值一定是二值化的

(2)有向距離場的生成,基於圖像空間的精確陰影,計算每個像素距離最近陰影邊界的有向距離,這是文中演算法的核心,也是後面風格化的基礎,在文中給出了一種有向距離場的計算方法,當然也可以採用其他方案

(3)有向距離場的高斯模糊,這一步是抽象陰影生成的關鍵

(4)過濾,通過一個轉移函數,將模糊後的有向距離場重新映射為陰影圖

(5)使用過濾後的陰影進行光照計算

整個演算法的流程,圖3,4中紅色部分表示陰影內部,藍色表示陰影外部

可以清楚地看出整個演算法流程的核心是(2)(3)(4),其中(2)是在整個圖像空間計算有向距離場,文中給出的有向距離場公式[13]如下:

D_{p}(V(x))=(int_{C}^{}frac{1}{left| x-y 
ight|^{p} dy} )^{-1/p}/(int_{C}dy^{} )^{-1/p}

文中p的取值為8,按照文中的說法,這個距離計算方法相較於歐幾里得距離,在精確性(accuracy)和平滑度(smoothness)上有一個比較好的折衷(trade off)。這裡C表示的是所有陰影的邊界像素的合集(邊界就是黑白髮生變化的位置),分母上的積分項表示的是整個邊界的長度,是一個歸一化參數,離散化來看,就是屏幕上所有邊界像素的個數

從上面這個計算公式就可以知道,如果要精確計算每個像素的有向距離,則需要針對每個像素遍歷整個圖像空間中的其他像素,找到所有是邊界的像素,並代入上述積分中進行求和運算,這個計算是比較低效的,因此文中採用的方法是在當前像素周圍隨機取一些像素參與到有向距離的計算,然後用計算結果去估計精確的有向距離的值,也就是所謂的蒙特卡洛方法。此外left| x - y 
ight| 在文中使用的是一個三維的歐幾里得距離,因此實際上計算這個有向距離時還需要一張深度圖。

計算出有向距離場後,接著要做的就是對這張圖進行模糊,可以想見,如果直接針對visibility圖進行模糊的話,得到的實際上是柔化的軟陰影,而不是我們想要的抽象陰影,所謂抽象陰影就是把精確陰影中的一些細節給略去,恰好就對應了模糊有向距離的值。這一步是用一個標準的高斯模糊去完成的,參數alpha表示高斯模糊的方差,這個值越大,則模糊程度越高,細節丟失越多,抽象程度越高。在文中的也使用了蒙特卡洛方法來減少高斯模糊的採樣次數。

在得到了經過模糊的有向距離場後,接下來就是如何把模糊後的有向距離場重新映射回陰影值,這裡用了一個很巧妙的轉移函數,一次性完成了邊界膨脹/腐蝕,亮度和柔和度的操作:

f(D)=(1- b)^{-1}smoothstep(D, i - s/2, i+s/2) + b

其中,b是亮度,表示陰影區域的亮度值(非陰影區域亮度值是1),D是經過模糊處理的有向距離值,s表示柔和度,換句話說表示了亮度從b到1過渡的區域寬度,也就是軟陰影的寬度,i表示了膨脹或者腐蝕的強度,正值表示膨脹,負值表示腐蝕,0表示不膨脹也不腐蝕。

上圖是這個轉移函數的圖像,結合有向距離場的定義再來直觀地看這個轉移函數其實很好理解,i可以理解為等高線的值,我們認為有向距離值小於i的都是陰影區域,b作為最低亮度很好理解,smoothstep的功能是讓陰影邊界不再是躍變(如果是step就變成了躍變)而是有一定過渡,過渡的區間中點由i決定,區間長度則由s來決定。

上述演算法中計算量最大的部分是有向距離場的生成,因為最終效果和採樣數量關係密切,因此很難做到完全實時,這大概也是《軍團要塞2》最終沒有集成這個演算法的原因。

總結

本來是想簡單的就卡通渲染做一個概述,寫著寫著就扯了一堆相干和不相干的技術。總的來說這篇文章偏綜述性質,沒有涉及到太多具體的實現,實現部分可以參考我在引用中列出的一些文章。卡通渲染這個領域從我的理解來看,是一個經驗大於理論,美術大於演算法的領域,因此應該主動地接受更多地trick,不要過度地思考「為什麼」,畢竟效果好看,直觀上好理解即可。關於卡通渲染的在本文中的分類,可能不是一個準確的描述,但是也提供了一種思考角度:有那麼多的NPR相關技術,在一個具體的項目中我究竟要使用其中的哪些技術?這時候具體的畫風要求就變得格外重要,因為畫風決定了具體技術的選擇和創新。文中的許多內容屬於我自己對一些資料的解讀,可能有許多理解上的錯誤,歡迎指正,另外今後我也會盡量避免寫這麼長的內容。。。因為碼字真的不輕鬆:(

引用

[1] A Trip Down The LOL Graphics Pipeline

[2] 西川善司[GUILTY GEAR Xrd -SIGN-]的[純卡通動畫的實時3D圖形]前篇

[3] 西川善司[GUILTY GEAR Xrd -SIGN-]的[純卡通動畫的實時3D圖形]後篇

[4] Cel Shading - Wikipedia

[5] Amy Gooch, Bruce Gooch, Peter Shirley, Elaine Cohen. A non-photorealistic lighting model for automatic technical illustration

[6] Pascal Barla, Jo?lle Thollot, Lee Markosian. X-toon: an extended toon shader

[7] Jason Mitchell, Moby Francke, Dhabih Eng. Illustrative Rendering in Team Fortress 2

[8] 風格化角色渲染實踐 - 拳四郎

[9] Jason Mitchell, Gary McTaggart, Chris Green. Shading in Valve』s Source Engine

[10] Ken-ichi Anjyo, Katsuaki Hiramitsu. Stylized Highlights for Cartoon Rendering and Animation

[11] Christopher DeCoro, Forrester Cole, Adam Finkelstein, Szymon Rusinkiewicz. Stylized Shadows

[12] 【NPR】卡通渲染

[13] Jianbo Peng, Daniel Kristjansson, Denis Zorin. Interactive Modeling of Topologically Complex Geometric Detail

[14] Drew Card, Jason L. Mitchell. Non-Photorealistic Rendering with Pixel and Vertex Shaders


推薦閱讀:

神奇的深度圖:複雜的效果,不複雜的原理
基於物理的渲染—基於球面調和基的實時全局光照明
電影工業中的流體模擬(四)- 納維斯托克斯方程(下)
利用Stencil來優化局部後處理特效
在中國科幻電影人才缺乏時,是否能通過設立電影物理學專業,實現彎道接近國外頂尖水準?

TAG:计算机图形学 |