unitychan是日本unity官方團隊提供的一個Demo,裏面有很好的卡通渲染效果,值得參考學習函數
上圖是我整理出來的shader結構,能夠看到Unity娘被拆分紅了不少個小的部件,我想主要是爲了掛動態骨骼吧。由於有不少部件的材質,shader其實都是同樣的能夠合併成少數幾個學習
我打算分3個部分來學習3d
本篇先寫第一部分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 );
效果以下,邊緣較白
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
// 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通道作差值後會發現,大部分區域的反射顏色都不見了,由於大部分是黑色。只有少數白色區域,能看到一些反射效果
這裏是計算其餘物體投射在身上的陰影,這裏插入了一個自定義的陰影顏色,爲了明顯一點我直接調成純黑,而後弄了一個物體擋住了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裏面則是採用了一張邊緣光貼圖
用N.L計算出來的值域在[-1,1]之間,*0.5+1後,轉成[0,1]區間,而後對這張1維的rim圖採樣。
咱們從右邊打一盞平行燈,來看看採樣的效果。跟光向量夾角越小的值越接近1,也就越白
可是邊緣光效果要收到漫反射影響,因此他這裏乘上了以前採樣出來的漫反射漸變,能夠看到高光被控制在了邊緣
falloffU = saturate( rimlightDot * falloffU );
CharaMain.cginc中用了不少的小技巧實現了漫反射、高光、反射、邊緣光。她沒有傳統卡通渲染賽璐璐的陰影漸變,也沒有油膩的高光。她的效果都有種欲出還收的感受。
我仍是以爲效果太淡了一些