Unity制作二次元材质角色
回到目录
大家好,我是阿赵。
这里继续讲二次元角色材质的制作。这次是讲头部的做法。
1、脸部
之前在分析资源的时候,其实已经发现了这个模型的脸部法线有问题,导致在做光照模型的时候,脸部很奇怪。
把fbx文件导入到3DsMax里面,可以发现
这个模型为了做口里面的牙齿和舌头的动画,把脸部的布线做得很极限。
然后尝试把整个脸打同一个光滑组,发现脸部的法线还是不能正常过渡,于是检查一下模型脸部的顶点,发现很多部位并没有焊接在一起。
所以要解决脸部的问题,首先要先处理一下原始的模型,把该焊接的点给焊接一下。
进入点层级,全选所有点,然后焊接一下。
接下来给整个脸打一个光滑组
到这里,这个模型的脸部结构暂时来说比较正常。但由于脸部的结构特殊性,所以如果我们真的按照光照模型的NDotL去计算光影,脸部的光影在某些角度会非常的恐怖,比如这样:
之前我写过2篇文章去解决这个问题,各位有兴趣可以去看看:
1.使用阈值图修改角色脸部阴影
2.用MaxScript修改模型法线映射
这里我选择了第2种方法去处理。
可以看到,集中在脸部的地方,法线非常的密集,而且方向比较的杂乱。这其实是正常而且合理的,因为脸部的转折很多。但用这些法线来计算光影,效果会很奇怪。
使用我之前用MaxScript写的映射法线的工具,进行一下球形法线映射
现在脸部的法线就会变成很平滑的过渡,方向非常的统一。这样的法线对于脸部来说其实是不对的,但是在做光影的时候,会比较的正常。然后配合一下之前ILM贴图的G通道,把脸部的阴影减淡一些,就可以。
这样看起来,脸部的影子就正常很多了。
这样看起来,脸部的
2、眼镜
如果只是赋予正常的身体材质,眼镜是这样的。
我们希望眼镜的效果大概有这些:
1.有基本颜色
2.有透明度,起码可以看到眼睛
3.有高光
4.有反射
所以我在身体材质的基础上做了一些修改:
1.使用正常的透明混合Blend SrcAlpha OneMinusSrcAlpha
2.使用了BlinnPhong高光
3.还是用Matcap做假反射,不过找了一个和眼镜本身颜色比较接近的Matcap反射球
完整Shader:
Shader "azhao/ToonGlass"
{
Properties
{
_BaseMap ("BaseMap", 2D) = "white" {}
_specColor("specColor",Color) = (1,1,1,1)
_shininess("shininess", Range(1 , 100)) = 1
_SpecAdd("SpecAdd",float) = 1.0
_MatCapTex("MatCapTex", 2D) = "white" {}
_MatCapIntensity("MatCapIntensity",Range(0,2)) = 1
_MatCapPow("MatCapPow",Range(0,5)) = 1
_MatCapUVScale("MatCapUVScale",Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float3 worldPos :TEXCOORD2;
float3 worldNormal :TEXCOORD3;
};
sampler2D _BaseMap;
float4 _BaseMap_ST;
float4 _specColor;
float _shininess;
float _SpecAdd;
sampler2D _MatCapTex;
float _MatCapIntensity;
float _MatCapPow;
float _MatCapUVScale;
//获取HalfLambert漫反射值
float GetHalfLambertDiffuse(float3 worldPos, float3 worldNormal)
{
float3 lightDir = UnityWorldSpaceLightDir(worldPos);
float NDotL = saturate(dot(worldNormal, lightDir));
float halfVal = NDotL * 0.5 + 0.5;
return halfVal;
}
//获取BlinnPhong高光
float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
{
float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
float specDir = max(dot(normalize(worldNormal), halfDir), 0);
float specVal = pow(specDir, _shininess);
return specVal;
}
float2 GetMatCapUV(float3 normalWorld)
{
float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
return normalView.xy*0.5 + 0.5;
}
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
o.uv2 = TRANSFORM_TEX(v.uv2, _BaseMap);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
half4 frag (v2f i) : SV_Target
{
// sample the texture
half4 col = tex2D(_BaseMap, i.uv);
half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);
float2 MatCapUV = GetMatCapUV(i.worldNormal)*_MatCapUVScale;
float4 MatCapCol = tex2D(_MatCapTex, MatCapUV)*_MatCapIntensity;
MatCapCol = pow(MatCapCol, _MatCapPow);
half3 finalRGB = col.rgb + _specColor * specVal*_SpecAdd;
finalRGB = finalRGB * (0.5) +MatCapCol.rgb*0.5;
half alpha = col.a;
return half4(finalRGB,alpha);
}
ENDCG
}
}
}
到这里,这个二次元卡通角色的材质基本上就做完了。下面说一下在这个例子里面,由于资源的原因没有做到的一些细节。虽然很遗憾不能做出效果,但也从理论上说一下应该怎样做吧。
1、头发
一般来说,头发如果有单独的问题贴图和法线贴图,比较容易做出一束一束的感觉。
可惜我这个模型素材里面,头发的贴图并没有单独的,只有一个和皮肤一样的baseMap,所以只能做到一片一片的效果。
然后也可以稍微从高光的角度去调整一下。
之前用于身体部分的高光的光照模型是BlinnPhong,所以光照会是一片的,我们可以试试改成Anisortropic各向异性高光。
//获取Anisortropic各向异性高光
float GetAnisortropicSpec(float3 worldPos, float3 worldNormal, float3 worldTangent)
{
float3 binormal = cross(worldNormal, worldTangent);
float3 lerpVal = normalize(lerp((worldNormal + binormal), binormal, _tangentVal));
float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
float anistropicVal = dot(lerpVal, halfDir);
float specDir = max(anistropicVal*anistropicVal, 0);
float specVal = pow(specDir, _shininess);
return specVal;
}
各向异性的高光一般是用于做一条一条形状的高光,不过由于我这个模型实在有点过于光滑,也没有法线贴图,所以这种感觉不是很明显。
2、眼睛
为了方便观察,先把眼镜隐藏了
之前我们做内描线的时候,其实发现了一个特别的东西,就是这个眼睛的贴图,居然不是在baseMap,而是在ILM贴图的A通道
在这个眼球或者瞳孔的地方,我虽然加上了高光和Matcap,但发现眼睛他其实并没有什么效果。这是因为,这里的瞳孔其实只是2个平面,在平面上面,不论是高光还是Matcap都不可能出现眼球的球形高光变化。
这个时候,要么把眼球改成真的球形,要么,使用法线贴图。
如果有法线贴图。法线贴图的做法是先采样一个凹进去的部分,然后给他赋予颜色,再把法系翻转,采样一个凸出来的部分,在凸出来的部分上面做高光和Matcap。
由于我手上这个模型并没有提供法线贴图,所以这部分我暂时就演示不了。