先贴效果:
unity毛发梳理
大致思路:
底层皮肤 + 多层毛发;采样Flowmap做偏移;绘制flowmap图达到实时预览的效果。
毛发在网上有很多的知识分享就不顾多赘述了贴一个链接,来说说Flowmap。Flowmap是存储了水平方向上流动方向的图,可以简单理解为一个矢量场的可视化。那么只要根据贴图中存储的矢量信息,就能得到偏移值。绘制贴图我这里是直接拿T4M改的,核心算法是求出上一个点位和下一个点位的信息,通过减法计算求出矢量,并且保存到贴图中。这样就达到了所画即所得,比美术使用其他DCC软件要方便很多。
其他:
需要优化,如贴图压缩,或是采样顶点色来代替贴图,在顶点阶段做计算。下面贴着色器代码(未优化),文件也贴上吧,但是没有整理,很乱很乱。
链接:https://pan.baidu.com/s/1-EJ6vrtcnGUoLEl0n2j5Gw?pwd=1234
提取码:1234
相关着色器:
cginc:
#ifndef CUSTOM_INCLUDED
#define CUSTOM_INCLUDED
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAl;
float3 tangent : TANGENT;
};
struct v2f
{
float4 uv : TEXCOORD0;
//UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 tangent : TANGENT;
float4 lightMul : TEXCOORD1;
float4 lightAdd : TEXCOORD2;
};
sampler2D _SkinTex;
sampler2D _Noise;
sampler2D _FurTex;
sampler2D _FlowMap;
float4 _SkinTex_ST;
float4 _Noise_ST;
float4 _FurTex_ST;
float4 _FlowMap_ST;
float4 _FurColor;
float4 _OcclusionColor;
float4 _SpecColor1;
float4 _SpecColor2;
float4 _FresnalColor;
float4 _UVOffset;
float4 _SpecInfo;
float _FurLength ;
float _FurRadius ;
float _AOOffset ;
float _AOPower ;
float _Timing ;
float _LightAdd ;
float _empbias ;
float _empscale ;
float _emPow ;
//切线偏移函数
float3 ShiftTangent (float3 T, float3 N, float shift)
{
float3 shiftT = T + shift * N;
return normalize(shiftT);
}
//高光计算函数
float StrandSpecular(float3 T, float3 V, float3 L,float exponent)
{
float3 H = normalize(L+V);
float dotTH = dot(T,H);
float sinTH = sqrt(1-dotTH*dotTH);
float dirAtten = smoothstep(-1,0,dotTH);
return dirAtten * pow(sinTH,exponent);
}
//皮毛
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex + v.normal * FURSTEP * _FurLength );
o.uv.xy = TRANSFORM_TEX(v.uv,_FurTex);
o.uv.zw = TRANSFORM_TEX(v.uv,_Noise);
//o.uv.xy = TRANSFORM_TEX(v.uv, _FurTex);
//float2 uvoffset = FURSTEP * _UVOffset.xy * 0.1;
//o.uv.zw = TRANSFORM_TEX(v.uv,_Noise) + uvoffset;
//光照
//环境光
fixed3 atten = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
float3 WorldNormal= UnityObjectToWorldNormal(v.normal);
float3 WorldLightDir = normalize(WorldSpaceLightDir(v.vertex));
float3 WorldViewDir = normalize(UnityWorldSpaceViewDir(mul(unity_ObjectToWorld, v.vertex + v.normal * FURSTEP * _FurLength).xyz));
float3 worldTangent = normalize(mul(unity_ObjectToWorld,v.tangent).xyz);
float3 worldBiTangent = normalize(cross(WorldNormal,worldTangent));//计算副切线
float diff = dot(WorldNormal,WorldLightDir) * 0.5 + 0.5;
//矫正光
diff = saturate(diff + FURSTEP + _LightAdd );
//轮廓光
float empricial = max(0,min(1,_empbias + _empscale * pow(saturate(1-dot(WorldViewDir,WorldNormal)),_emPow)));
o.lightAdd.a = empricial;
fixed3 diffuse = _LightColor0.rgb * diff + atten;
//AO
fixed occlusionColor = lerp(1,1-pow(1-FURSTEP,_AOPower),_AOOffset);
//高光
float3 T1 = ShiftTangent(worldBiTangent,WorldNormal,_SpecInfo.y*0.1);
float3 T2 = ShiftTangent(worldBiTangent,WorldNormal,_SpecInfo.w*0.1);
float Spec1 = StrandSpecular(T1,WorldViewDir,WorldLightDir,_SpecInfo.x*16);
float Spec2 = StrandSpecular(T2,WorldViewDir,WorldLightDir,_SpecInfo.z*16);
o.lightAdd.rgb = (_SpecColor1.rgb*Spec1+_SpecColor2.rgb*Spec2) * FURSTEP * 2 * saturate(dot(WorldNormal,WorldLightDir));
o.lightMul.rgb = diffuse * occlusionColor;
o.lightMul.a = 1;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//flowmapUV偏移
#ifdef _UVMODE_UV
float2 uvoffset = FURSTEP * _UVOffset.xy * 0.1;
i.uv.zw += uvoffset;
#elif _UVMODE_FLOWMAP
float2 uvoffset = tex2D(_FlowMap , i.uv.xy).rg*2-1;
i.uv.zw += _UVOffset.w * uvoffset * FURSTEP * 0.1;
#endif
//基础颜色
fixed3 albedo = tex2D(_FurTex,i.uv.xy).rgb * _FurColor.rgb;
//透明度
fixed alpha = saturate(tex2D(_Noise,i.uv.zw).r*2 - (FURSTEP * FURSTEP + (FURSTEP * _FurRadius)) * _Timing);
alpha = saturate(alpha-FURSTEP*_FurRadius);
//光照
fixed3 finalColor = albedo * i.lightMul.rgb + i.lightAdd.rgb * alpha + i.lightAdd.a * _FresnalColor;
return fixed4(finalColor ,alpha);
}
//毛皮
v2f vert_skin(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX( v.texcoord,_SkinTex);
o.uv.zw = 1;
fixed3 atten = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 WorldNormal= mul((float3x3)unity_ObjectToWorld,v.normal);
float3 WorldLightDir = normalize(WorldSpaceLightDir(v.vertex));
float diff = dot(WorldNormal,WorldLightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * diff + atten;
o.lightMul.rgb = diffuse;
o.lightMul.a = 1;
o.lightAdd = 0;
return o;
}
fixed4 frag_skin(v2f i) : SV_TARGET
{
fixed3 albedo = tex2D(_SkinTex , i.uv.xy).rgb * i.lightMul.rgb ;
fixed alpha = tex2D(_SkinTex , i.uv.xy).a;
return fixed4(albedo,alpha);
}
#endif
shader:
Shader "Unlit/furpass"
{
Properties
{
_SkinTex ("SkinTex", 2D) = "white" {}
_FurTex ("FurTex", 2D) = "white" {}
_FurColor("FurColor",COLOR) = (1,1,1,1)
_AOOffset ("AOOffset ",range(0,1)) = 0.1
_AOPower ("AOPower ",float) = 3
_UVOffset("UVOffset",vector) = (0,0,0,0)
_Noise ("Noise", 2D) = "white" {}
_FurLength ("FurLength ",float) = 0.1
_FurRadius ("FurRadius ",float) = 0.1
_Timing ("FurTiming ",float) = 0.1
_LightAdd ("矫正光 ",float) = 0.1
//轮廓光
_FresnalColor("FresnalColor",COLOR) = (1,1,1,1)
_empbias ("_empbias ",float) = 0.1
_empscale ("_empscale ",float) = 0.1
_emPow ("_emPow ",float) = 0.1
//高光
_SpecColor1("_SpecColor1",color) = (0,0,0,1)
_SpecColor2("_SpecColor2",color) = (0,0,0,1)
_SpecInfo("SpecInfo" , vector) = (1,0,1,0)
//Flowmap
//Shader中的属性
[KeywordEnum(UV,FLOWMAP)] _UVMODE("UV MODE" , float) = 0
_FlowMap("FlowMap" , 2D) = "black"{}
}
SubShader{
Tags{"LightMode"="ForwardBase" "RenderType" = "Transparent" "IgnoreProjector" = "True" "Queue" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
//毛皮
pass
{
CGPROGRAM
#pragma vertex vert_skin
#pragma fragment frag_skin
#define FURSTEP 0
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.05 //定义宏
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.1 //逐渐变大
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.15
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.2
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.25
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.3
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.35
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.4
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.45
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.5
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.55
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.6
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.65
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.7
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.75
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.8
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.85
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.9
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 0.95
#include "Furpass.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _UVMODE_UV _UVMODE_FLOWMAP
#define FURSTEP 1
#include "Furpass.cginc"
ENDCG
}
}
}