菜鸡shader:L7 人物模型材质的代码实现(Dota2食人魔)

文章详细介绍了Dota2模型中Shader的实现过程,包括代码细节、材质的处理和光影效果的实现。作者强调了美术直觉在编写代码中的重要性,特别是对图像的处理,如垂直翻转和图层合成。代码中涉及的关键概念包括漫反射、镜面反射、环境光和自发光等。同时,作者指出观察视角对最终效果的影响,并提到了代码中透切功能的问题和投影的不完美之处。
摘要由CSDN通过智能技术生成

这节课感觉代码细节很多,一些东西还是需要结合美术方面的直观感受来写代码,还是得感叹一句美术转技术美术真的很自然。

下面就简单放一下我的代码了,感觉也没什么好说的,一些比较重要的东西也都在代码里打上注释了,最主要的是下面这几张图:

    1. 贴图组成
      在这里插入图片描述
      在这里插入图片描述
    1. PS处理
      在这里插入图片描述
    • 需要注意的是有些图是歪的,像是武器的SpecularMask, RimMask, TintByBaseMask和SpecularExponent这四张图全都是歪了。需要在ps里面进行垂直翻转,然后再合成一张图。
    • 最后三个fresnel的图是直接拉成了长宽大小的图,因为他们只是u坐标不一样,v坐标都是一样的。原本的长宽应该是这样的:
      在这里插入图片描述
    1. 材质的shader实现思路图
    • OldSchoolPro模型。为了防止和之前的课会有混淆,下面先放一下之前的shader思路图。跟之前的不一样的。之前的shader是将模型分为直接光照和环境光两个主要的部分,在光照和环境光下细分为漫反射和镜面反射去渲染。
      在这里插入图片描述
    • Dota2模型。在Dota2材质里是将模型分为漫反射和镜面反射两个主要部分去渲染,然后再在镜面反射和漫反射下分为直接光照和环境光进行渲染,然后再加上一些其他的细节,轮廓光自发光啥的。
      在这里插入图片描述

下面就直接放上代码了:

Shader "shader forge/Dota_weapon"
{
    Properties
    {
        [Header(Texture)]   [Space]
        _MainTex ("RGB With A(A:Cutout)", 2D) = "white" {}
        _MaskTex("R:SpecInt G:RimInt B:TintMask A:SpecPow",2D) = "black"{}
        _NormTex("RGB:Normal",2D) = "bump"{}
        _MetalnessMask("Metalness Mask",2D) = "black"{}
        _EmissionMask("Emission Mask",2D) = "black"{}
        _DiffWrapTex("Diffuse Color Wrap Tex",2D) = "gray"{}
        _FresWrapTex("Fresnel Wrap Tex",2D) = "gray"{}
        _Cubemap("Cube Map",Cube) = "_Skybox"{}

        [Header(Diffuse_Dir)] [Space]
        _Light_Color("Light Color",Color) = (1.0,1.0,1.0,1.0)

        [Header(Diffuse_Env)] [Space]
        _EnvDiffCol ("Env Color",COLOR) = (1.0,1.0,1.0,1.0)
        _EnvDiffInt ("Env Color Intensity", Range(0.0,10.0)) = 1.0

        [Header(Specular_Dir)] [Space]
        _mySpecInt ("Specular Intensity", Range(0.0,10.0)) = 5
        _mySpecPow("Specular Power", Range(0.0,30.0)) = 5

        [Header(Specular_Env)] [Space]
        _EnvSpecInt ("Env Specular Intensity",Range(0.0,30.0)) = 1.0

        [Header(RimLight)] [Space]
        _RimCol ("Rim Color", Color) = (1.0,1.0,1.0,1.0)
        _RimInt ("Rim Intensity", Range(0.0,20.0)) = 1.0

        [Header(Emission)] [Space]
        _EmitInt ("Emission Intensity",Range(0.0,20.0)) = 1.0

        [HideInInspector]
        _Cutoff ("Cutoff", Range(0.0,1.0)) = 0.05
        [HideInInspector]
        _Color  ("Main Color",color)=(1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }       
        LOD 100

        Pass
        {
            Name "FORWARD"
            Tags{
                "RenderType" = "ForwardBase"
            }
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #include "Lighting.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
                float4 posWorld : TEXCOORD1;
                float3 nDirWS : TEXCOORD2;
                float3 tDirWS : TEXCOORD3;
                float3 bDirWS : TEXCOORD4;
                LIGHTING_COORDS(5,6)
            };

            //Texture
            uniform sampler2D _MainTex;
            uniform float4 _MainTex_ST;
            uniform sampler2D _MaskTex;
            uniform sampler2D _NormTex;
            uniform sampler2D _MetalnessMask;
            uniform sampler2D _EmissionMask;
            uniform sampler2D _DiffWrapTex;
            uniform sampler2D _FresWrapTex;
            uniform samplerCUBE _Cubemap;

            //Diffuse_Dir
            uniform float4 _Light_Color;

            //Diffuse_Env
            uniform float4 _EnvDiffCol;
            uniform float _EnvDiffInt;

            //Specular_Dir
            uniform half _mySpecInt;
            uniform half _mySpecPow;

            //Specular_Env
            uniform half _EnvSpecInt;

            //Rim 
            uniform half4 _RimCol;
            uniform half _RimInt;

            //Emission
            uniform half _EmitInt;

            //透切
            uniform half _Cutoff;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);
                o.nDirWS = UnityObjectToWorldNormal(v.normal);
                o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz,0.0)).xyz);
                o.bDirWS = normalize(cross(o.nDirWS,o.tDirWS) * v.tangent.w);
                TRANSFER_VERTEX_TO_FRAGMENT(o)
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {                
                float3 nDirTS = UnpackNormal(tex2D(_NormTex,i.uv)).rgb;

                //向量准备
                float3x3 TBN_Matrix = float3x3(i.tDirWS,i.bDirWS,i.nDirWS);
                //unity shader里的是列向量,列向量右乘矩阵时,结果是列向量
                //但是法线本身有特殊性,这里反过来乘是为了乘以矩阵的逆转置矩阵
                float3 nDirWS_FT = normalize(mul(nDirTS, TBN_Matrix));    
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
                float3 halfDir = normalize(lightDir + viewDir);
                float3 vrDirWS = normalize(reflect(-viewDir,nDirWS_FT));             

                //中间量准备
                half NoL = clamp(dot(nDirWS_FT,lightDir),0.0,1.0);
                half NoV = clamp(dot(nDirWS_FT,viewDir),0.0,1.0);                
                half NoH = clamp(dot(nDirWS_FT,halfDir),0.0,1.0);
                half VoR = clamp(dot(viewDir, vrDirWS),0.0,1.0);

                //贴图采样
                half4 var_MainTex = tex2D(_MainTex,i.uv);
                half4 var_MaskTex = tex2D(_MaskTex,i.uv);
                half var_Metalness = tex2D(_MetalnessMask,i.uv).r;
                half var_Emission = tex2D(_EmissionMask,i.uv).r;         
                half3 var_Fresnel = tex2D(_FresWrapTex,half2(NoV,0.3)).rgb;
                half3 var_Cubemap = texCUBElod(_Cubemap,float4(vrDirWS,lerp(8.0,0.0,var_MaskTex.a))).rgb;

                //提取信息
                half3 baseCol = var_MainTex.rgb;
                half opacity = var_MainTex.a;   //不透明度
                half specInt = var_MaskTex.r;
                half rimInt = var_MaskTex.g;
                half specTint = var_MaskTex.b;
                half specPow = var_MaskTex.a;
                half metalness = var_Metalness;
                half emitInt = var_Emission;
                half3 envCube = var_Cubemap;
                half3 shadow = LIGHT_ATTENUATION(i);                

                //光照模型
                    //漫反射和镜面反射,两个被不同的变量控制染色程度
                    half3 diff_col = lerp(baseCol,float3(0.0,0.0,0.0),metalness);
                    half3 spec_col = lerp(baseCol,float3(0.3,0.3,0.3),specTint);    //0.3是经验值
                    //Fresnel
                    half3 fresnel = lerp(var_Fresnel,0.0,metalness);
                    half fres_col = fresnel.r;  //罕见,这里不使用
                    half fres_rim = fresnel.g;  //轮廓光用的Fresnel
                    half fres_spec = fresnel.b; //镜面反射光用的fresnel
                    //漫反射和镜面反射下都分为主光和环境光
                        // 1.0 漫反射的主光
                        half halfLambert = NoH * 0.5 + 0.5;     //半兰伯特
                        half3 diffuseWrapTex = tex2D(_DiffWrapTex,half2(halfLambert,0.2)).rgb;
                        half3 dir_diffuseCol = diff_col * diffuseWrapTex * _Light_Color;
                        // 1.1 漫反射的环境光
                        half3 env_diffuseCol = diff_col * _EnvDiffCol * _EnvDiffInt;
                        // 2.0 镜面反射的主光
                        half phong = pow(max(VoR,0.0),specPow * _mySpecPow);
                        half spec = phong * max(0.0, NoL);     //phong和lambert相乘,意思是在漫反射比较黑的地方,镜面反射也弱一些
                        spec = max(spec, fres_spec);    //跟菲尼尔的高光进行混合,这里是使用取最大值
                        spec = spec * _mySpecInt;
                        half3 dir_specularCol = spec * spec_col * _Light_Color;
                        // 2.1 镜面反射的环境光
                        half envSpecInt = max(fres_spec,metalness) * specInt;
                        half3 env_specularCol = spec_col * envSpecInt * envCube * _EnvSpecInt;
                    //轮廓光rim 
                    half3 rimCol = _RimCol * fres_rim * rimInt * max(0.0,nDirWS_FT.g) * _RimInt;
                    //half3 test = _RimCol * _RimInt * rimInt * max(0.0,nDirWS_FT.g);
                    //自发光Emission
                    half3 emitColor = diff_col * emitInt * _EmitInt;

                //返回值
                half3 finalColor = (dir_diffuseCol + dir_specularCol) * shadow + (env_diffuseCol + env_specularCol) + rimCol + emitColor;
                //clip(opacity - _Cutoff);    //透切,没大过阈值的都抛弃掉
                return float4(finalColor,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
    //FallBack "Legacy Shaders/Transparent/Cutout/VertexLit"
}

放一下效果图,上面的代码我注释掉了透切,也就是clip那一行,因为这个武器的Translucency贴图是全黑的,就中间一个白点,用了透切这行代码就啥也没有了。老师那个我也没明白咋回事,应该是自己画了贴图。
在这里插入图片描述
shader效果:
在这里插入图片描述

然后我试了一下模型主体,也就是食人魔模型,效果如下:
在这里插入图片描述

  • 这里需要注意下,我们要从Game窗口去观察,因为这是从摄像机视角出发观察世界的,我们的shader的里的菲涅尔采样也是根据摄像机的观察方向采样的,从Game窗口看才是正常效果。
  • 如果不是从Game,而是直接从Scene窗口看,会得到一个油光水亮的结果,这里让我疑惑了半天,想了下果然是观察视角的问题。油光水亮就像下面这样:
    在这里插入图片描述
    在这里插入图片描述

不完美的缺点

有个缺点,就像我代码最后写的FallBack,只有用Diffuse的时候,投影才正常。前面好几节课的作业都是投影问题,我查了一圈感觉可能是版本的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值