Sparkle Shader – 闪烁亮片材质

Last modified date

两种方法用躁点图配合Bloom后期效果做出闪烁亮片材质。

闪耀亮片的基本原理很简单:做法就是用一张躁点图,做pow运算,让少数躁点亮度提高,配合post-processing中的bloom效果,让少数躁点亮爆。

但其实具体做各种材质的时候,有很多不同的手段可以实现亮片效果。

方式一:在fragment阶段做normalDir躁点扰动。

做法就是用一张彩色Noise贴图,在fragment阶段将normalDir干扰。然后与viewDir或者lightDir做dot运算(受视点影响或者光源影响),最后再做一次Pow运算。

如下图,基于视点的亮片,所以中间亮片较大,边缘的亮片较小。

进行100次幂运算以后,亮片变得很细密了,比较符合沙砾的效果。

这种方式适合本身没有其他类似diffuse的光源亮暗,所以必须自己计算Sparkle的亮度变化。

方式二:在一个基于“Diffuse + Specular + Rim”的材质上增加亮片。

先做一个有Diffuse + Specular + Rim效果的shader。

因为shader里已经分别提供了Diffuse,Specular和Rim部分的值,所以就没必要在Sparkle里计算视角信息了。我直接把1张黑白的Noise贴图按2次不同的uv尺寸和偏移取值,相乘,再做pow运算,可获得少量亮片Sparkle。跟方法一的区别是这种本身没有亮暗变化。

最后把Sparkle分别乘以Diffuse,Specular和Rim的亮度系数,可以自由调整在不同区域的躁点亮度。

其他效果:

用视差映射(Parallax Mapping)的方式增加3层亮片,距离渐远,可以做出类似果冻中的亮片效果。

具体做法中并没有用到视差贴图,因为不需要做什么立体凹凸,只是要由近到远显示3层亮片,所以只要利用Parallax Mapping的函数获得UV偏移值就好了。

Shader代码:
Shader "WalkingFat/Sparkle/Sparkle01"
{
    Properties
    {
        _Tint ("Tint", Color) = (0.5,0.5,0.5,1)
        _ShadowColor ("Shadow Color", Color) = (0,0,0,1)
        
        _NoiseTex ("Noise Texture", 2D) = "white" {}
        _NoiseSize ("Noise Size", Float) = 2
        _ShiningSpeed ("Shining Speed", Float) = 0.1
        _SparkleColor ("sparkle Color", Color) = (1,1,1,1)
        SparklePower ("sparkle Power", Float) = 10
        
        _Specular ("Specular", Range(0,1)) = 0.5
        _Gloss ("Gloss", Range(0,1) ) = 0.5
        
        _RimColor ("Rim Color", Color) = (0.17,0.36,0.81,0.0)
        _RimPower ("Rim Power", Range(0.6,36.0)) = 8.0
        _RimIntensity ("Rim Intensity", Range(0.0,100.0)) = 1.0
        
        _specsparkleRate ("Specular sparkle Rate", Float) = 6
        _rimsparkleRate ("Rim sparkle Rate", Float) = 10
        _diffsparkleRate ("Diffuse sparkle Rate", Float) = 1
        
        _ParallaxMap ("Parallax Map", 2D) = "white" {}
        _HeightFactor ("Height Scale", Range(-1, 1)) = 0.05
        
    }
    
    SubShader
    {
        Tags 
        {
            "RenderType"="Opaque" 
        }
        
        LOD 100
        
        Pass
        {
            Tags{ "LightMode" = "ForwardBase" }
 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            #include "AutoLight.cginc"
            #include "Lighting.cginc"
            #pragma multi_compile_fwdbase
            #pragma multi_compile_fwdadd_fullshadows
            #pragma multi_compile_fog
            
            //#pragma glsl
 
            sampler2D _NoiseTex, _ParallaxMap;
            float4 _NoiseTex_ST, _ParallaxMap_ST;
            float4 _Tint, _ShadowColor, _RimColor, _SparkleColor;
            float _Specular, _Gloss, _NoiseSize, _ShiningSpeed;
            float _RimPower, _RimIntensity, _specsparkleRate, _rimsparkleRate, _diffsparkleRate, SparklePower;
            float _HeightFactor;
 
            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
                float4 tangent : TANGENT;
            };
 
            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 posWorld : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
                float3 lightDir : TEXCOORD3;
                float3 viewDir : TEXCOORD4;
                float3 lightDir_tangent : TEXCOORD5;
                float3 viewDir_tangent : TEXCOORD6;
                LIGHTING_COORDS(7,8)
                UNITY_FOG_COORDS(9)
            };
            
            // caculate parallax uv offset
            inline float2 CaculateParallaxUV(v2f i, float heightMulti)
            {
                float height = tex2D(_ParallaxMap, i.uv).r;
                //normalize view Dir
                float3 viewDir = normalize(i.lightDir_tangent);
                //偏移值 = 切线空间的视线方向.xy(uv空间下的视线方向)* height * 控制系数
                float2 offset = i.lightDir_tangent.xy * height * _HeightFactor * heightMulti;
                return offset;
            }
 
            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos (v.vertex);
                o.normalDir = UnityObjectToWorldNormal (v.normal);
                o.posWorld = mul (unity_ObjectToWorld, v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _NoiseTex);
                
                o.viewDir = normalize(_WorldSpaceCameraPos.xyz - o.posWorld.xyz);
                o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                TANGENT_SPACE_ROTATION;
                o.lightDir_tangent = normalize(mul(rotation, ObjSpaceLightDir(v.vertex)));
                o.viewDir_tangent = normalize(mul(rotation, ObjSpaceViewDir(v.vertex)));
                
                UNITY_TRANSFER_FOG (o,o.pos);
                TRANSFER_VERTEX_TO_FRAGMENT (o)
                return o;
            }
 
            float4 frag(v2f i) : SV_Target
            {
                i.normalDir = normalize(i.normalDir);
                
                // attenuation
                float attenuation = LIGHT_ATTENUATION(i);
                float3 attenColor = attenuation * _LightColor0.xyz;
 
                // specular
                float specularPow = exp2 ((1 - _Gloss) * 10.0 + 1.0);
                float3 specularColor = float4 (_Specular,_Specular,_Specular,1);
               
                float3 halfVector = normalize (i.lightDir + i.viewDir);
                float3 directSpecular = pow (max (0,dot (halfVector, i.normalDir)), specularPow) * specularColor;
                float3 specular = directSpecular * attenColor;
                
                // sparkle
                float2 uvOffset = CaculateParallaxUV(i, 1);
                float noise1 = tex2D (_NoiseTex, i.uv * _NoiseSize + float2 (0, _Time.x * _ShiningSpeed) + uvOffset).r;
                float noise2 = tex2D (_NoiseTex, i.uv * _NoiseSize * 1.4 + float2 (_Time.x * _ShiningSpeed, 0)).r;
                float sparkle1 = pow (noise1 * noise2 * 2, SparklePower);
                
                uvOffset = CaculateParallaxUV(i, 2);
                noise1 = tex2D (_NoiseTex, i.uv * _NoiseSize + float2 (0.3, _Time.x * _ShiningSpeed) + uvOffset).r;
                noise2 = tex2D (_NoiseTex, i.uv * _NoiseSize * 1.4 + float2 (_Time.x * _ShiningSpeed, 0.3) + uvOffset).r;
                float sparkle2 = pow (noise1 * noise2 * 2, SparklePower);
                
                uvOffset = CaculateParallaxUV(i, 3);
                noise1 = tex2D (_NoiseTex, i.uv * _NoiseSize + float2 (0.6, _Time.x * _ShiningSpeed) + uvOffset).r;
                noise2 = tex2D (_NoiseTex, i.uv * _NoiseSize * 1.4 + float2 (_Time.x * _ShiningSpeed, 0.6) + uvOffset).r;
                float sparkle3 = pow (noise1 * noise2 * 2, SparklePower);
                
                // diffuse
                float NdotL = saturate (dot (i.normalDir, i.lightDir));
                float3 directDiffuse = NdotL * attenColor;
                float3 diffuseCol = lerp (_ShadowColor, _Tint, directDiffuse);
                
                // Rim
                float rim = 1.0 - max(0, dot(i.normalDir, i.viewDir));
                fixed3 rimCol = _RimColor.rgb * pow (rim, _RimPower) * _RimIntensity; 
                
                // final color
                fixed3 sparkleCol1 = sparkle1 * (specular * _specsparkleRate + directDiffuse * _diffsparkleRate + rimCol * _rimsparkleRate) * lerp(_SparkleColor, fixed3(1,1,1), 0.5);
                fixed3 sparkleCol2 = sparkle2 * (specular * _specsparkleRate + directDiffuse * _diffsparkleRate + rimCol * _rimsparkleRate) * _SparkleColor;
                fixed3 sparkleCol3 = sparkle3 * (specular * _specsparkleRate + directDiffuse * _diffsparkleRate + rimCol * _rimsparkleRate) * 0.5 * _SparkleColor;
                
                fixed4 finalCol = fixed4 (diffuseCol + specular + sparkleCol1 + sparkleCol2 + sparkleCol3 + rimCol, 1);
                
                UNITY_APPLY_FOG(i.fogCoord, finalCol);
                return finalCol;
            }
            ENDCG
        }
    }
    Fallback "VertexLit"
}

Share

This site is protected by wp-copyrightpro.com