基于flowmap的毛发梳理

本文详细介绍了如何在Unity中使用Flowmap实现毛发的实时偏移效果,包括底层皮肤、多层毛发处理,以及如何通过采样Flowmap并计算偏移值来达到所画即所得的视觉效果。还提到了着色器代码和一些优化建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先贴效果:

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
      }
   }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值