Created
December 10, 2019 05:52
-
-
Save eviltak/e04b83ffdb91aa3d477bbfe0ca370da7 to your computer and use it in GitHub Desktop.
A basic wet surface shader with puddles and ripples -- based on https://seblagarde.wordpress.com/2012/12/10/observe-rainy-world/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Shader "Realtime Reflections/Wet Shader" | |
{ | |
Properties | |
{ | |
_Color("Color", Color) = (1,1,1,1) | |
_MainTex("Albedo", 2D) = "white" {} | |
_Metallic("Metallic (R) Smoothness (A)", 2D) = "black" {} | |
_BumpMap("Normal Map", 2D) = "bump" {} | |
_BumpScale("Normal Map Scale", Range(0.0, 1.0)) = 1.0 | |
_Occlusion("Occlusion", 2D) = "white" {} | |
_OcclusionStrength("Occlusion Strength", Range(0.0, 1.0)) = 1.0 | |
_RippleTexture("Rain Normal Map", 2D) = "bump" {} | |
_PuddleMask("Puddle Mask (A)", 2D) = "white" {} | |
_PuddleMaskPower("Puddle Mask Power", Range(0.0, 1.0)) = 1.0 | |
_PuddleCrackPower("Puddle Crack Power", Range(0.0, 1.0)) = 1.0 | |
_DryAreas("Dry Areas (R)", 2D) = "black" {} | |
_Heightmap("Heightmap (A)", 2D) = "white" {} | |
_ReflectionTex ("Reflection Texture", 2D) = "white" {} | |
_ReflectionScale ("Reflection Intensity", Float) = 1 | |
_Cube("Environment Map", Cube) = "" {} | |
_Parallax ("Height", Range (0.005, 0.5)) = 0.03 | |
_Distortion ("Reflection Distortion", Range(0.0, 1.0)) = 0.25 | |
_Wetness("Wetness", Range(0.0, 1.0)) = 0.5 | |
_RainIntensity("Rain Intensity", Range(0.0, 1.0)) = 0.0 | |
[MaterialToggle] _IsPlane("Is Plane", Float) = 0 | |
} | |
Subshader | |
{ | |
Tags { "Queue" = "Geometry+1" } | |
ZWrite On | |
CGPROGRAM | |
#pragma target 3.0 | |
#pragma surface surf Standard | |
#define ONE_M(x) (1.0 - (x)) | |
#define SQR(x) (x * x) | |
#define PI (3.14159) | |
half _ReflectionScale; | |
half _BumpScale; | |
half _PuddleMaskPower; | |
half _PuddleCrackPower; | |
half _Distortion; | |
half _Parallax; | |
half _Wetness; | |
half _RainIntensity; | |
half _OcclusionStrength; | |
half _IsPlane; | |
half4 _Color; | |
uniform float4 _MainTex_TexelSize; | |
sampler2D _MainTex; | |
sampler2D _Metallic; | |
sampler2D _ReflectionTex; | |
sampler2D _BumpMap; | |
sampler2D _RippleTexture; | |
sampler2D _PuddleMask; | |
sampler2D _Heightmap; | |
sampler2D _Occlusion; | |
sampler2D _DryAreas; | |
samplerCUBE _Cube; | |
struct Input | |
{ | |
half2 uv_MainTex; | |
half2 uv_RippleTexture; | |
half2 uv_PuddleMask; | |
half2 uv_DryAreas; | |
half4 screenPos; | |
half3 worldRefl; | |
half3 viewDir; | |
INTERNAL_DATA | |
}; | |
half3 ComputeRipple(half2 UV, half CurrentTime, half Weight, half2 lambda) | |
{ | |
half4 Ripple = tex2Dlod(_RippleTexture, half4(UV, lambda)); | |
// We use multi sampling here in order to improve Sharpness due to the lack of Anisotropic Filtering when using tex2Dlod | |
Ripple += tex2Dlod(_RippleTexture, float4(UV.xy, lambda * 0.5)); | |
Ripple *= 0.5; | |
Ripple.gb = Ripple.gb * 2.0 - 1.0; // Decompress perturbation | |
half DropFrac = frac(Ripple.a + CurrentTime); // Apply time shift | |
//DropFrac = ddx(DropFrac) + ddy(DropFrac); | |
half TimeFrac = DropFrac - 1.0f + Ripple.r; | |
half DropFactor = saturate(0.2f + Weight * 0.8f - DropFrac); | |
half FinalFactor = DropFactor * Ripple.r * | |
sin( clamp(TimeFrac * 9.0f, 0.0f, 3.0f) * PI); | |
return half3(Ripple.gb * FinalFactor * 0.35f, 1.0f); | |
} | |
half3 GetRippleNormal(half2 UVRipple, half4 Times, half2 lambda) | |
{ | |
// We enable one layer by quarter intensity and progressively blend in the | |
// current layer | |
half4 Weights = _RainIntensity - half4(0, 0.25, 0.5, 0.75); | |
Weights = saturate(Weights * 4); | |
// Generate four shifted layer of animated circle | |
half3 Ripple1 = ComputeRipple(UVRipple + half2( 0.25f,0.0f), Times.x, Weights.x, lambda); | |
half3 Ripple2 = ComputeRipple(UVRipple + half2(-0.55f,0.3f), Times.y, Weights.y, lambda); | |
half3 Ripple3 = ComputeRipple(UVRipple + half2(0.6f, 0.85f), Times.z, Weights.z, lambda); | |
half3 Ripple4 = ComputeRipple(UVRipple + half2(0.5f,-0.75f), Times.w, Weights.w, lambda); | |
// Compose normal of the four layer based on Weights | |
half4 Z = lerp(half4(1.0, 1.0, 1.0, 1.0), half4(Ripple1.z, Ripple2.z, Ripple3.z, Ripple4.z), Weights); | |
half3 Normal = half3( Weights.x * Ripple1.xy + Weights.y * Ripple2.xy | |
+ Weights.z * Ripple3.xy + Weights.w * Ripple4.xy, | |
Z.x * Z.y * Z.z * Z.w); | |
// return result | |
return normalize(Normal); | |
} | |
half3 WaterNormal(half2 uv, half2 lambda) | |
{ | |
half4 TimeMul = half4(1.0f, 0.85f, 0.93f, 1.13f); | |
half4 TimeAdd = half4(0.0f, 0.2f, 0.45f, 0.7f); | |
half4 Times = (_Time.y * TimeMul + TimeAdd) * 1.6f; | |
Times = frac(Times); | |
half3 ripple = GetRippleNormal(uv, Times, lambda); | |
return lerp(half3(0, 0, 1), ripple, saturate(_RainIntensity * 100)); | |
} | |
half SpecularToMetallic(half3 specular) | |
{ | |
return saturate(dot(specular, 0.33) * 1000 - 500); | |
} | |
void WetProcess(inout half3 albedo, inout half smoothness, half wetLevel, half metallic) | |
{ | |
half porosity = saturate((ONE_M(smoothness) - 0.5) / 0.4); | |
// Calc diffuse factor | |
half factor = lerp(1, 0.2, ONE_M(metallic) * porosity); | |
albedo *= lerp(1.0, factor, wetLevel); | |
smoothness = lerp(1.0, smoothness, lerp(1, factor, 0.5 * wetLevel));; | |
} | |
void surf(Input IN, inout SurfaceOutputStandard o) | |
{ | |
half4 p = tex2D(_PuddleMask, IN.uv_PuddleMask); | |
half h = tex2D(_Heightmap, IN.uv_MainTex).r; | |
//Get levels | |
const half MERGE_PUDDLE = 0.1; | |
half wetLevel = saturate((_Wetness - ONE_M(p.a)) / MERGE_PUDDLE);; | |
half2 accumWaters; | |
accumWaters.x = min(_PuddleCrackPower, ONE_M(h)); | |
accumWaters.y = saturate((_PuddleMaskPower - ONE_M(p.a)) / MERGE_PUDDLE); | |
half puddleMask = max(accumWaters.x, accumWaters.y); | |
//Parallax | |
half2 offset = ParallaxOffset (h, _Parallax, IN.viewDir); | |
IN.uv_MainTex += offset; | |
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb * _Color; | |
half4 metallic = tex2D(_Metallic, IN.uv_MainTex); | |
o.Metallic = metallic.r * 0.0;//for now | |
o.Smoothness = 0.0; | |
half newWetLevel = saturate(wetLevel + puddleMask); | |
WetProcess(o.Albedo, o.Smoothness, newWetLevel, o.Metallic); | |
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_MainTex)) * _BumpScale; | |
//Lerp | |
o.Smoothness = lerp(o.Smoothness, 1, puddleMask); | |
// Water F0 specular is 0.02 (based on IOR of 1.33) | |
o.Metallic = lerp(o.Metallic, SpecularToMetallic(0.02), puddleMask); | |
//Rain | |
half2 dx1 = ddx(IN.uv_MainTex * 512 * 0.75); | |
half2 dy1 = ddy(IN.uv_MainTex * 512 * 0.75); | |
half d = max(dot(dx1, dx1), dot(dy1, dy1)); | |
half2 lambda = 0.5 * log2(d); | |
half3 waterNormal = WaterNormal(IN.uv_RippleTexture, lambda); | |
o.Normal = lerp(o.Normal, waterNormal, puddleMask); | |
//Reflection | |
half4 reflection; | |
half roughness = ONE_M(o.Smoothness); | |
if(_IsPlane) { | |
half2 uv_Ref = IN.screenPos.xy / IN.screenPos.w; | |
uv_Ref += o.Normal * _Distortion; | |
reflection = tex2Dlod(_ReflectionTex, half4(uv_Ref, 0.0, | |
pow(roughness, 0.75) * UNITY_SPECCUBE_LOD_STEPS * 8)); | |
} | |
else { | |
reflection = texCUBElod(_Cube, half4(WorldReflectionVector(IN, o.Normal), | |
pow(roughness, 0.75) * UNITY_SPECCUBE_LOD_STEPS * 8)); | |
} | |
half nv = DotClamped(IN.viewDir, o.Normal); | |
half oneMinusReflectivity; | |
half3 specColor; | |
DiffuseAndSpecularFromMetallic(o.Albedo, o.Metallic, specColor, oneMinusReflectivity); | |
half grazingTerm = saturate(o.Smoothness + ONE_M(oneMinusReflectivity)); | |
half rim = FresnelLerpFast(specColor, grazingTerm, nv); | |
reflection *= _ReflectionScale * pow(rim, 0.75); | |
o.Albedo += reflection; | |
o.Occlusion = tex2D(_Occlusion, IN.uv_MainTex).r * _OcclusionStrength; | |
} | |
ENDCG | |
} | |
Fallback "Standard" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment