Unity醬~ 卡通渲染技術分析(一)

前面的話

unitychan是日本unity官方團隊提供的一個Demo,裏面有很好的卡通渲染效果,值得參考學習函數

Shader分類

上圖是我整理出來的shader結構,能夠看到Unity娘被拆分紅了不少個小的部件,我想主要是爲了掛動態骨骼吧。由於有不少部件的材質,shader其實都是同樣的能夠合併成少數幾個學習

我打算分3個部分來學習3d

  1. CharaMain.cginc 主要用於衣服等材質
  2. CharaSkin.cginc 皮膚效果
  3. Hair 頭髮、眼睛、睫毛等部位的渲染

CharaMain.cginc

本篇先寫第一部分body材質。CharaMain.cginc中包含漫反射、高光、反射、邊緣光、陰影等效果的實現,接下來咱們詳細拆解code

基礎着色效果(模擬漫反射)

這裏是用視角向量跟法線點積(但這樣的作法就不會受光照角度變化),而後用結果採樣一張相似這樣的衰減紋理orm

// Falloff. Convert the angle between the normal and the camera direction into a lookup for the gradient
    float_t normalDotEye = dot( normalVec, i.eyeDir.xyz );
    float_t falloffU = clamp( 1.0 - abs( normalDotEye ), 0.02, 0.98 );
    float4_t falloffSamplerColor = FALLOFF_POWER * tex2D( _FalloffSampler, float2( falloffU, 0.25f ) );
    float3_t shadowColor = diffSamplerColor.rgb * diffSamplerColor.rgb;
    float3_t combinedColor = lerp( diffSamplerColor.rgb, shadowColor, falloffSamplerColor.r );
    combinedColor *= ( 1.0 + falloffSamplerColor.rgb * falloffSamplerColor.a );

FalloffSampler
效果以下,邊緣較白
漫反射blog

但我感受加上採樣顏色後效果不是很明顯,邊緣顏色會深一些。
加上採樣顏色ci

高光效果

// Use the eye vector as the light vector
    float4_t reflectionMaskColor = tex2D( _SpecularReflectionSampler, i.uv.xy );
    float_t specularDot = dot( normalVec, i.eyeDir.xyz );
    float4_t lighting = lit( normalDotEye, specularDot, _SpecularPower );
    float3_t specularColor = saturate( lighting.z ) * reflectionMaskColor.rgb * diffSamplerColor.rgb;
    combinedColor += specularColor;

這個高光的計算很奇葩,也是用法線跟視角向量的點積,最後的效果就是有一點淡淡的高光。也是跟真正的燈光角度無關的。it

這裏還用到了一張貼圖,高光會用到這張圖的rgb通道,後面要寫的反射,會用到這張圖的a通道。
大概就是描出來那些地方高光強一些io

RGB通道

高光效果

反射

// Reflection
float3_t reflectVector = reflect( -i.eyeDir.xyz, normalVec ).xzy;
float2_t sphereMapCoords = 0.5 * ( float2_t( 1.0, 1.0 ) + reflectVector.xy );
float3_t reflectColor = tex2D( _EnvMapSampler, sphereMapCoords ).rgb;
reflectColor = GetOverlayColor( reflectColor, combinedColor );

combinedColor = lerp( combinedColor, reflectColor, reflectionMaskColor.a );
combinedColor *= _Color.rgb * _LightColor0.rgb;
float opacity = diffSamplerColor.a * _Color.a * _LightColor0.a;

這裏是採樣一張環境貼圖,爲啥用張這樣的圖呢?我以爲他主要是爲了能反射出這種銀色的色調罷了。你想要什麼色調就換啥樣的環境圖。
ast

GetOverlayColor這個函數是用來融合自身貼圖顏色,跟反射環境貼圖顏色的。裏面用了不少小技巧

先來看看直接輸出反射貼圖是什麼樣

反射貼圖

經過GetOverlayColor融合後的反射顏色
融合後

而後這裏會用到高光反射貼圖的A通道,來表示某些區域的反射的強度。
Alpha通道

用alpha通道作差值後會發現,大部分區域的反射顏色都不見了,由於大部分是黑色。只有少數白色區域,能看到一些反射效果
最終反射效果

接收陰影處理

這裏是計算其餘物體投射在身上的陰影,這裏插入了一個自定義的陰影顏色,爲了明顯一點我直接調成純黑,而後弄了一個物體擋住了unity醬~ 效果如圖

使用LIGHT_ATTENUATION指令對陰影貼圖採樣而且返回數據供你使用。若是你想知道LIGHT_ATTENUATION指令具體作了些什麼,檢查 AutoLight.cginc文件

#ifdef ENABLE_CAST_SHADOWS
    // Cast shadows
    shadowColor = _ShadowColor.rgb * combinedColor;
    float_t attenuation = saturate( 2.0 * LIGHT_ATTENUATION( i ) - 1.0 );
    combinedColor = lerp( shadowColor, combinedColor, attenuation );
#endif

邊緣高光

終於這裏有一個跟着光照角度變換的效果了,哈哈

通常咱們會使用菲尼爾效應來實現邊緣光,而unitychan裏面則是採用了一張邊緣光貼圖

RimLight

用N.L計算出來的值域在[-1,1]之間,*0.5+1後,轉成[0,1]區間,而後對這張1維的rim圖採樣。

咱們從右邊打一盞平行燈,來看看採樣的效果。跟光向量夾角越小的值越接近1,也就越白

邊緣光貼圖採樣

可是邊緣光效果要收到漫反射影響,因此他這裏乘上了以前採樣出來的漫反射漸變,能夠看到高光被控制在了邊緣

falloffU = saturate( rimlightDot * falloffU );

rim * 漫反射漸變

完整效果

完整效果

總結

CharaMain.cginc中用了不少的小技巧實現了漫反射、高光、反射、邊緣光。她沒有傳統卡通渲染賽璐璐的陰影漸變,也沒有油膩的高光。她的效果都有種欲出還收的感受。

我仍是以爲效果太淡了一些

相關文章
相關標籤/搜索