Skip to content

Instantly share code, notes, and snippets.

@eviltak
Created December 10, 2019 05:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eviltak/e04b83ffdb91aa3d477bbfe0ca370da7 to your computer and use it in GitHub Desktop.
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/
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