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中用了不少的小技巧实现了漫反射、高光、反射、边缘光。她没有传统卡通渲染赛璐璐的阴影渐变,也没有油腻的高光。她的效果都有种欲出还收的感受。
我仍是以为效果太淡了一些