Shader "Custom/WaterFoamParticle" { Properties { _BumpSphereScale ("Sphere Normal Scale", Float) = 1.0 _BumpNoiseScale ("Noise Normal Scale", Float) = 1.0 _ShadowOffsetMult ("Shadow Offset Mult", Range(0, 1)) = 0.5 _MainTex ("Noise", 2D) = "white" {} _EdgeToNoiseFreq ("Edge To Noise Freq", Float) = 5.0 _NoiseStrength ("Noise Strength", Range(0, 1)) = 1.0 _Radius ("Radius", Range(0, 1)) = 1.0 _Sharpness ("Sharpness", Range(1, 20)) = 1.0 _Scroll ("Scroll", Range(0, 1)) = 1.0 //[Header(Colors)] //_FoamTint ("Foam Tint", Color) = (1,1,1,1) [Header(Lighting Hacks)] _FoamIndirectLightMultiplier ("Foam Ambient Multiplier", Range(0, 20)) = 7 [Header(Material Properties)] _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Water vertex:vert alpha:premul #pragma target 3.0 #define _ALPHAPREMULTIPLY_ON 1 #include "Lighting.cginc" #include "Shadows.cginc" #include "UnityPBSLighting.cginc" // Textures sampler2D _MainTex; // Colors fixed3 _FoamTint; // Values half _BumpSphereScale, _BumpNoiseScale; half _NoiseStrength; half _ShadowOffsetMult; half _EdgeToNoiseFreq; half _Radius; half _Sharpness; half _FoamIndirectLightMultiplier; float _Scroll; fixed _Glossiness; fixed _Metallic; struct appdata_t { float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3; fixed4 color : COLOR; }; struct Input { float2 uv_MainTex; float2 rawUV; float4 color : COLOR; float4 screenPos; float3 worldPos; float3 worldNormal; INTERNAL_DATA half size; }; struct SurfaceOutputWater { fixed3 Albedo; // base (diffuse or specular) color float3 Normal; // tangent space normal, if written half3 Emission; half Metallic; // 0=non-metal, 1=metal // Smoothness is the user facing name, it should be perceptual smoothness but user should not have to deal with it. // Everywhere in the code you meet smoothness it is perceptual smoothness half Smoothness; // 0=rough, 1=smooth half Occlusion; // occlusion (default 1) fixed Alpha; // alpha for transparencies // Added: fixed ShadowAttenuation; }; void vert (inout appdata_t v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input,o); o.rawUV = v.texcoord.xy; o.size = v.texcoord.z; } inline half4 LightingWater (SurfaceOutputWater s, float3 viewDir, UnityGI gi) { s.Normal = normalize(s.Normal); // shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha) // this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha s.Albedo *= s.Alpha; // Apply shadow to light contribution gi.light.color *= s.ShadowAttenuation; // Boost indirect lighting in shadow gi.indirect.diffuse *= _FoamIndirectLightMultiplier; // Remove specular/reflection from foam gi.indirect.specular = 0; half4 c = UNITY_BRDF_PBS (s.Albedo, 0, 0, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect); c.a = s.Alpha; return c; } inline void LightingWater_GI (SurfaceOutputWater s, UnityGIInput data, inout UnityGI gi) { #if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal); #else Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic)); gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal, g); #endif } float3 GetNormalFromPattern (float pattern, float scale, float2 uv) { float4 xyzw = float4(ddx(uv), ddy(uv)); float determinant = 1 / (xyzw.x * xyzw.w - xyzw.y * xyzw.z); float4 inverse = float4(xyzw.w, -xyzw.y, -xyzw.z, xyzw.x) * determinant; float2 deriv = float2(-ddx(pattern), -ddy(pattern)); float2 transformed = float2(dot(deriv, inverse.xy), dot(deriv, inverse.zw)); return normalize(float3(transformed, 1.0 / scale)); } void surf (Input IN, inout SurfaceOutputWater o) { fixed2 dirFromCenter = IN.rawUV - 0.5; float radial = 1 - 2 * length(dirFromCenter); float2 noiseUV = IN.uv_MainTex + IN.color.xy; noiseUV += frac(float2(_Time.z, _Time.w) * _Scroll); float noise = tex2D(_MainTex, noiseUV).a - 0.5; // Shape float val = lerp (radial - 0.5 + _Radius, 0.5 + (noise) * _EdgeToNoiseFreq, _NoiseStrength); val = lerp(0.5, val, _Sharpness); // Reduce based on particle alpha half sizeOverLifetime = IN.color.a; half maxPossibleValue = lerp(0.5 + _Radius, 0.5 + 0.4 * _EdgeToNoiseFreq, _NoiseStrength); maxPossibleValue = lerp(0.5, maxPossibleValue, _Sharpness); val -= (1 - sizeOverLifetime) * maxPossibleValue; // Restrict to circular area and smoothen val *= max(radial, smoothstep(0, 1, radial * _Sharpness * 0.5)); val = sqrt(val); val = smoothstep(0, 1, val); // Calculate normal float heightForNormal = _BumpSphereScale / length(float3(dirFromCenter, 1)) + noise * _EdgeToNoiseFreq * _BumpNoiseScale; float3 normal = GetNormalFromPattern(heightForNormal, 1, IN.rawUV); // World space variables fixed offset = (1 - val) * IN.size * _ShadowOffsetMult; float3 positionWS = IN.worldPos; float3 viewDirWS = normalize(UnityWorldSpaceViewDir(positionWS)); float3 normalRawWS = WorldNormalVector (IN, float3(0, 0, 1)); float headOn = 0.1 + 0.9 * saturate(dot(viewDirWS, normalRawWS)); // Shadow float3 shadowOffset = -viewDirWS; float shadowAttenuation = GetSunShadowsAttenuation_PCF5x5(positionWS + shadowOffset * offset, IN.screenPos.z, 0).x; // Output o.Albedo = 0.5; o.Alpha = val; o.Normal = normal; //o.Fade = val; o.ShadowAttenuation = shadowAttenuation; o.Metallic = _Metallic; o.Smoothness = _Glossiness; } ENDCG } }