Sparkle Shader – 闪烁亮片材质
两种方法用躁点图配合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"
}