卡通渲染(英文Cel Shading或者Toon shading),是一種能夠使3D的圖像產(chǎn)生動漫效果的渲染手段。它具體的定義我們先看一段來自維基的解釋:
Cel shading or toon shading is a type of non-photorealistic rendering designed to make 3-D computer graphics appear to be flat by using less shading color instead of a shade gradient or tints and shades. Cel-shading is often used to mimic the style of a comic bookor cartoon and/or give it a characteristic paper-like texture.
(卡通渲染(英語:Cel-shading或者Toon Shading)是一種去真實感的渲染方法,旨在使電腦生成的圖像呈現(xiàn)出手繪般的效果检柬。為了使圖像可以與漫畫或者卡通達(dá)到形似的效果,專業(yè)人員通常使用卡通渲染著色器進(jìn)行處理)
總結(jié)來說,卡通渲染是一種NPR(非真實感繪制)任柜,可以使用在一些3D的日系二次元風(fēng)格的手游或者端游中,使得畫面的質(zhì)感更加接近動漫中的感覺沛厨。
比較的有名的使用卡通渲染的游戲宙地,這里我必須提一下崩壞三(崩壞學(xué)園3)。每次看到崩崩崩的視頻逆皮,總是會被它的畫風(fēng)給驚嘆到宅粥。米忽悠真不愧是被游戲耽誤的動畫公司。崩壞三大量使用了卡通渲染技術(shù)电谣,這一點在米哈游的技術(shù)總監(jiān)賀甲對于崩壞3的技術(shù)演講中可以了解到秽梅。B站有關(guān)于這個演講的視頻(不過是日語的,因為是在Unite Tokyo 2018上的演講剿牺,貌似被官方翻譯了)风纠。文字版本的可以百度。
這種風(fēng)格對于喜歡動漫的人來說簡直是完美牢贸,恨不得把自己的所有游戲都加上一個卡通渲染竹观。那么,卡通渲染具體是怎么實現(xiàn)的呢?原理又是什么臭增?
簡單來說懂酱,在正常的渲染著色過程中,由于使用了真實的光照模型誊抛,在模型上的顏色是隨著光照的角度漸變的列牺,而卡通渲染為了模擬動畫繪制時候的風(fēng)格,根據(jù)每個像素的法線和光照的方向的關(guān)系拗窃,來使得這片區(qū)域的像素投影到其中一個明暗區(qū)域上面瞎领,達(dá)到多段離散的明暗區(qū)域的效果。
接下來我們將分別在Unreal Engine 4(UE4)中和WebGL中來實現(xiàn)簡單的Cel Shading随夸,可以選擇自己感興趣的閱讀九默,其中WebGL涉及shader代碼。
Unreal Engine 4
在UE4當(dāng)中宾毒,我們甚至不需要寫shader的代碼驼修,只需要通過材質(zhì)的藍(lán)圖編輯器就能夠?qū)崿F(xiàn)卡通渲染的效果。首先先新建一個Material诈铛。然后在Material Domain中選擇PostProcessing,這樣這個材質(zhì)就會被作為后處理的材質(zhì)乙各,能夠作用在計算了光照和顏色的模型上面。
接下來就是在藍(lán)圖中實現(xiàn)卡通渲染的細(xì)節(jié)了幢竹。在UE4中耳峦,我們無法直接獲得光照的信息,這里需要使用一些手段來獲得光照LightMap焕毫。
UE4中可以通過SceneTexture來獲得一些有用的信息蹲坷,在SceneTextureID中可以選擇其類型
這里我們需要一個PostProcessingInput0來獲得場景中應(yīng)用了光照和后處理之后的顏色,再添加一個SceneTexture節(jié)點并且選擇DiffuseColor來獲得場景中初始(無光照和后處理)的顏色咬荷,用一個divide節(jié)點來從PostProcessingInput0中除去DiffuseColor獲取LIghtMap的灰階值冠句。
當(dāng)然,可以先先將兩個輸出連接DeSaturation的節(jié)點來更徹底地將顏色去除幸乒。
我們將獲得的結(jié)果映射到0-1的范圍內(nèi)
通過這個結(jié)果懦底,我們可以大概地了解到對于每個像素點的光照信息。這樣我們就可以根據(jù)光照信息來計算最后的顏色值罕扎。
這里我采用了最簡單的方法聚唐,只有兩種明暗差別,如果之前獲得結(jié)果大于0.5腔召,則最后的顏色信息直接為DIffuseColor杆查,否則要將DiffuseColor乘一個系數(shù)。最后的整個藍(lán)圖如下圖顯示
當(dāng)然你可以將結(jié)果更加細(xì)分(WebGL的部分中就更加細(xì)分)臀蛛,這樣結(jié)果就會有更多段的明亮度亲桦。之后在場景中添加一個PostProcessingVolume并且把整個后處理材質(zhì)添加到PostProcessingVolume中就可以作用到場景上崖蜜。
設(shè)置PostProcessingVolume中的Infinite Extend為Infinite,整個場景就會被PostProcessingVolume給覆蓋客峭,添加的后處理材質(zhì)就能夠作用到整個場景上
這里你可能會發(fā)現(xiàn)結(jié)果有點和想象中的不一樣豫领。記住,在卡通渲染的藍(lán)圖選項中把Blendable location選為Before tonemapping
關(guān)于Blendable location的解釋舔琅,我們可以在官網(wǎng)中看到這個介紹
簡單來說等恐,Before tonemapping能夠作為在HDR的顏色上,而After tonemapping只剩下LDR的顏色备蚓,會失去很多細(xì)節(jié)课蔬。至于Translucency(透明度)相關(guān)的選項,Before translucency作用在Before tonemapping之前郊尝。
假如你想要把卡通渲染只作用在一些模型上而不是整個場景二跋。這時就要使用custom depth buffer了。把需要使用卡通渲染的模型中的render custom depth pass勾選上
然后修改剛才的藍(lán)圖為如下
可以看到虚循,我們主要添加了對于depth buffer的判斷同欠,根據(jù)custom depth buffer和scene depth的關(guān)系來進(jìn)行最后明亮度的取舍样傍。
到目前為止横缔,我們都是將顏色乘一個自定義的系數(shù)來表明明亮度。更好的方法是使用一個材質(zhì)查詢表(Texture LUT<look-up table>)衫哥。
這樣就能夠使用計算出來的值通過查表來乘相應(yīng)的系數(shù)茎刚。
到此,超簡單版本的卡通渲染就完成了撤逢,結(jié)果大概是這個樣子膛锭。當(dāng)然如果你是把它作用在整個場景上的話,可能會丟失光照蚊荣。場景的背景就是黑色的初狰,下圖是只用在了人物的模型上,就很動畫互例。
參考資料:
- Anime-Look Cel Shading in UE4(貌似是一個日本學(xué)生寫的奢入,模型就是用他推薦的,強推)
- Unreal Engine 4 Cel Shading Tutorial(這個人的很多Unreal教程都不錯)
WebGL
Webgl上面的實現(xiàn)就很算法了媳叨,沒用什么好的模型腥光,webgl的一個好處在于不需要像opengl什么的要去配置一下環(huán)境,只要一個支持webgl的瀏覽器就能跑了糊秆。而且這個教程還給了一個在線的編輯器(cyos)武福,直接在上面能夠像在IDE里面一樣寫shader的代碼,能直接運行展示結(jié)果痘番,并且把結(jié)果下載下來捉片,對于新手特別友好平痰。
流程和UE4中的基本沒什么兩樣,只是用代碼實現(xiàn)了伍纫。下面直接上shader代碼:
Vertex shader(頂點著色器)
precision highp float;
// Attributes
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
// Uniforms
uniform mat4 world;
uniform mat4 worldViewProjection;
// Varying
varying vec3 vPos;
varying vec3 vNormal;
varying vec2 vUV;
void main(void) {
gl_Position = worldViewProjection * vec4(position, 1.0);
vUV = uv;
vPos = vec3(world * vec4(position, 1.0));
vNormal = normalize(vec3(world * vec4(normal, 1.0)));
}
在vertex shader中觉增,我們把UV,世界坐標(biāo)系下的頂點坐標(biāo)和正規(guī)化后的法線計算出來傳給pixel shader翻斟,以便后面的光照計算逾礁。
Pixel (fragment)shader(片元著色器)
precision highp float;
// Lights
varying vec3 vPos;
varying vec3 vNormal;
varying vec2 vUV;
// Refs
uniform sampler2D textureSampler;
void main(void) {
float celShadingThreshold[4];
celShadingThreshold[0] = 0.02;
celShadingThreshold[1] = 0.1;
celShadingThreshold[2] = 0.6;
celShadingThreshold[3] = 0.9;
float celShadingVal[5];
celShadingVal[0] = 0.1;
celShadingVal[1] = 0.5;
celShadingVal[2] = 0.87;
celShadingVal[3] = 0.95;
celShadingVal[4] = 1.0;
vec3 lightPos = vec3(0, 5, 20);
vec3 lightDir = normalize(lightPos - vPos);
float angle = dot(lightDir, vNormal);
vec3 color = texture(textureSampler, vUV).rgb;
if (angle < celShadingThreshold[0])
{
color = color * celShadingVal[0];
}
else if (angle < celShadingThreshold[1])
{
color = color * celShadingVal[1];
}
else if (angle < celShadingThreshold[2])
{
color = color * celShadingVal[2];
}
else if (angle < celShadingThreshold[3])
{
color = color * celShadingVal[3];
}
else
{
color = color * celShadingVal[4];
}
gl_FragColor = vec4(color, 1.0);
}
片元著色器中,通過計算法線和光線的點積(光源的點是自定義的)访惜,我們得到一個浮點數(shù)結(jié)果嘹履,使用這個結(jié)果去查詢一個系數(shù)表,我們可以知道最后顯示的顏色是材質(zhì)貼圖中采樣得到的顏色乘一個多大的系數(shù)债热。這樣通過頂點的法線和光線的夾角的不同砾嫉,就能使得其明亮度落入不同的區(qū)域,造成手繪的質(zhì)感窒篱。
結(jié)果大概是這個樣子
結(jié)論
以上焕刮,我們已經(jīng)完成了初步的簡單卡通渲染,當(dāng)然想要達(dá)到崩崩崩的那種效果還任重而道遠(yuǎn)墙杯,除了要模型上面的配合外配并,至少還要加一個描邊。描邊又是一個大話題高镐,我現(xiàn)在也還在學(xué)習(xí)怎么給模型描邊(sobel溉旋,laplacian算子)。所以嫉髓,待續(xù)中观腊。
(以上首發(fā)于知乎)