最近在制作游戏,在研究shader的时候发现我并不适合使用shader graph这种图形化的编程,于是想用代码写一个着色器(就像OpenGL的着色器那样),然后就发现可能shader更有操作空间。
我想写一个Toon 风格的shader,网上当然其实有很多教程,但是我发现即使是三渲二这样子的材质,大部分写法还是严格遵循phong模型进行渲染,也就是最终着色效果受固有色,光照强度(法线和光线方向)和环境光的影响。当然这样子渲染出来的模型更逼真,但是也少了一点风格化,因为在不使用光线追踪的情况下,表达出环境间接光照也比较复杂。
上文提到了少了一点风格化,其实是因为在绘画过程中经常可以看到“藏色”,实际上就是光追可以呈现的间接光照所呈现的效果,在暗面尤其突出。
环境间接光照可能会导致最终渲染的颜色和物体固有色处于不同色相,然而固有色和“灰度”的叠加往往达不到这种效果,所以我干脆在同一个材质上设置了两个阈值(与法线和光线方向的点乘相比),最终划分出三个区域(高光,灰,暗),每个区域的颜色均可以编辑,不受色相的限制。
这是最终的效果(邦尼):
这是其中一个材质球的参数:
可以看出来二、三个区域完全不属于同一个色相。
其实只需要在片元着色器那里做一些改变就可以了。
fixed4 frag (v2f i) : SV_Target
{
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
atten = lerp(1.0, atten, _ShadowStrength);
// 从这里开始
float NdotL = dot(normalize(i.worldNormal), normalize(_WorldSpaceLightPos0.xyz));
NdotL = max(NdotL, 0.0);
fixed4 color;
if (NdotL > _Specular)
color = _ColorBright;
else if (NdotL > _Mid)
color = _ColorMid;
else
color = _ColorDark;
// 到这里结束
color.rgb *= atten;
UNITY_APPLY_FOG(i.fogCoord, color);
return color;
}
根据点乘结果和阈值划分区域,点乘结果就是NdotL,阈值就是_Specular和_Mid两个参数。