在上篇中介绍了绘制融球角色的核心函数,通过光线步进和SDF函数我们获得了融球的隐式表面并且计算出了法线和深度,此篇中我们将开始制作材质和表情。
1. 光照模型实现
//===============Lighting====================
//直接光镜面反射
float4 highlight = 1;
highlight.rgb = step(_HighLightPow*0.1+0.9, dot(N, halfDir) * dot(N, halfDir)) * lCol;
highlight.a = step(_HighLightPow*0.1+0.9, dot(N, halfDir) * dot(N, halfDir));
#if defined(_ISHIGHLIGHT_OFF)
highlight = 0;
#endif
//环境光镜面反射
float2 MatCapuv = cross(normalize(TransformWorldToView(pos)),TransformWorldToView(N));
MatCapuv = float2(MatCapuv.x,MatCapuv.y*-1)*0.5+0.5;
float EnvFresnelRate = Fresnel(N,rayDir,_EnvSpecBios,_EnvSpecInt,_EnvSpecPower);
float3 EnvSpec = SAMPLE_TEXTURE2D(_CubeMap, smp, MatCapuv) * _EnvSpecInt * EnvFresnelRate;
//漫反射
float3 hLambert = (dot(N, L)*0.5+0.5)*baseColor+_EnvColor;
#if defined(_ISENVSPEC_OFF)
EnvSpec = 0;
#endif
float3 finalColor = EnvSpec+highlight+hLambert;
2. 表情贴图采样
2.1 UV
采样一张格子图的结果。
//表情由模型空间坐标xy值采样贴图实现
float3 modelPos = TransformWorldToObject(pos);
float2 posUV = modelPos.xy * _TestTex_ST.xy + _TestTex_ST.zw;
由于没有真实的uv空间我们可以拿已有的数据组成uv,如模型空间下的顶点的x,y值,缺点是中间会有拉伸,尽量避免将贴图内容覆盖到这块区域。
2.2 眼睛
对UV的x,y值进行偏移,这里的实现逻辑跟帧动画的制作类似,都是走格子。 上图是UV走格子时行数与列数和自变量x的关系。
//眼睛
float eyeMask = step(_EmotionRange, 1 - distance(posUV, 0));
float2 baseEeysUV = ((modelPos.xy * 0.5) + 0.5) * _EyesTex_ST.xy + _EyesTex_ST.zw;
leftEyeUV = baseEeysUV * 0.5 + float2(0.4, 0.45);
leftEyeUV.x += frac(_EyesType.x / 2);
leftEyeUV.y += floor(_EyesType.x / 2) / 4;
rightEyeUV = (float2(1 - baseEeysUV.x, baseEeysUV.y)) * 0.5 + float2(0.4, 0.45);
rightEyeUV.x += frac(_EyesType.y / 2);
rightEyeUV.y += floor(_EyesType.y / 2) / 4;
float4 lefEyeColor = SAMPLE_TEXTURE2D(_EyesTex, smp, leftEyeUV.xy);
float4 rightEyeColor = SAMPLE_TEXTURE2D(_EyesTex, smp, rightEyeUV.xy);
lefEyeColor.rgb = lerp(0, 1, lefEyeColor.b) + lerp(0, _EyesColor1, lefEyeColor.r) + lerp(
0, _EyesColor2, lefEyeColor.g);
rightEyeColor.rgb = lerp(0, 1, rightEyeColor.b) + lerp(0, _EyesColor1, rightEyeColor.r) + lerp(
0, _EyesColor2, rightEyeColor.g);
float4 eyesColor = (rightEyeColor + lefEyeColor) * eyeMask;
这是一张PS绘制的眼睛贴图,rgb三个通道分别控制了眼睛的不同部分方便调整颜色。 克苏鲁卡比
遮罩形状
值得注意的是为了之渲染一对眼睛,我在这里以uv原点为中心取一个圆形遮罩,去掉范围之外的眼睛。
2.3 嘴巴
//嘴巴
float mouthMask = step(_EmotionRange + 0.01, 1 - distance(float2(posUV.x, posUV.y + 0.04), 0));
float2 baseMouthUV = ((modelPos.xy * 0.5) + 0.5) * _MouthTex_ST.xy + _MouthTex_ST.zw;
mouthUV.x = baseMouthUV.x + frac(_MouthType / 2);
mouthUV.y = baseMouthUV.y + floor(_MouthType / 2) / 2;
float4 mouthColor = SAMPLE_TEXTURE2D(_MouthTex, smp, mouthUV) ;
与眼睛采样类似的采样嘴巴贴图,并给出嘴巴范围遮罩。
3.Decal阴影实现
这里的Decal阴影采用Unity自带的Decal工具实现。