Divergent/mods/Screen Space Shaders/gamedata/shaders/r3/water.ps

226 lines
7.5 KiB
PostScript

/**
* @ Version: SCREEN SPACE SHADERS - UPDATE 18
* @ Description: Water implementation
* @ Modified time: 2023-09-30 10:46
* @ Author: https://www.moddb.com/members/ascii1457
* @ Mod: https://www.moddb.com/mods/stalker-anomaly/addons/screen-space-shaders
*/
#include "common.h"
#include "anomaly_shaders.h"
#include "reflections.h"
#include "lmodel.h"
// Screen space functions
#include "check_screenspace.h"
#include "screenspace_water.h"
#ifdef SSFX_FOG
#include "screenspace_fog.h"
#endif
struct vf
{
float2 tbase : TEXCOORD0; // base
float4 tnorm0 : TEXCOORD1; // nm0
float3 position_w : TEXCOORD2; // nm1
float3 M1 : TEXCOORD3;
float3 M2 : TEXCOORD4;
float3 M3 : TEXCOORD5;
float3 v2point_w : TEXCOORD6;
float4 tctexgen : TEXCOORD7;
float4 c0 : COLOR0;
float fog : FOG;
float4 hpos : SV_Position;
};
Texture2D s_nmap;
Texture2D s_bluenoise;
Texture2D s_rainsplash;
Texture2D s_watercaustics;
uniform float4 ssfx_is_underground;
//float3 water_intensity; // Unused?
float4 main( vf I ) : SV_Target
{
// 3d view space pos reconstruction math
float3 Pv = float3(I.tctexgen.z * (I.hpos.xy * pos_decompression_params.zw - pos_decompression_params.xy), I.tctexgen.z);
// Normal Textures
float3 n0 = s_nmap.Sample(smp_base, I.tnorm0.xy);
float3 n1 = s_nmap.Sample(smp_base, I.tnorm0.zw);
float3 basenormal = ((n0 + n1) * 0.5f) * 2.0f - 1.0f;
basenormal += float3(0.1f, 0.1f, 0.1f); // Offset the normal to "center" the textures again
// Load Data
float2 PosTc = I.tctexgen.xy / I.tctexgen.z;
gbuffer_data gbd = gbuffer_load_data( PosTc, I.hpos.xy );
float4 _P = float4( gbd.P, 1.0f );
// Water Surface to world space
float3 w_s = mul(m_inv_V, Pv );
// Trick to check if rain can fall on the water, gloss include the rain_apply_gloss
float rain_cover = step(0.035f * (-0.4f + rain_params.x), gbd.gloss - 0.04f );
// Rain intensity. Fadeout and rain cover
half is_raining = rain_params.x > 0 ? 1.0f : 0.0f;
float RainInt = clamp(rain_params.x * 1.6f, 0.65f * is_raining, 1.0f);
// Ripples normal
float2 Ripples = ssfx_rain_ripples( s_rainsplash, (w_s.xz + eye_position.xz) * 0.13f, float3(RainInt, G_SSR_WATER_RAIN, 15.0f), _P.z);
basenormal += float3(Ripples, 0) * is_raining;
// Water wave intensity
float3 Navg = normalize(float3(basenormal.x * G_SSR_WATER_WAVES, basenormal.y * G_SSR_WATER_WAVES, basenormal.z));
// Refractions normal ( Used by shadows and refraction )
float2 N_Scr = normalize(float3(basenormal.x * G_SSR_WATER_REFRACTION, basenormal.y * G_SSR_WATER_REFRACTION, basenormal.z));
// Discard refractions from things above the water ( Like weapons )
float Refraction_Discard = Pv.z < SSFX_get_depth(PosTc + N_Scr, 0);
// Screen UV + Discard
float2 Refraction_UV = N_Scr * Refraction_Discard;
// Shadows to do some tricks
#ifndef USE_MSAA
float water_shadows = saturate(s_accumulator.Sample(smp_nofilter, PosTc + Refraction_UV).r * 2000);
#else
float water_shadows = saturate(s_accumulator.Load(int3((PosTc + Refraction_UV) * screen_res.xy, 0), 0).r * 2000);
#endif
float3 Nw = mul (float3x3(I.M1, I.M2, I.M3), Navg);
Nw = normalize (Nw);
float3 v2point = normalize (I.v2point_w);
float3 vreflect = reflect(v2point, Nw.xyz);
float3 eyedir = normalize(Pv);
float3 Nv = normalize(mul(m_V, Nw));
// Some vars to put our reflection
float3 refl_ray, refl_skybox;
float3 reflection;
float3 ssr_hit_uv = 0.0f;
#ifdef NEED_REFLECTIONS
// Blue Noise & Normal for noise
float3 NN = normalize(float3(basenormal.x * 0.15f, basenormal.y * 0.15f, basenormal.z));
float blue_noise = s_bluenoise.Sample(smp_linear, I.tbase * float2(0.05f, 0.05f) + NN).b * 1.5f;
// Compute reflection bounce
float3 wreflect = reflect(eyedir, Nv);
// Don't trace rays which face the camera. Still worth to avoid the rays mess when you look down.
float edgeFade = step(dot(-eyedir, wreflect), 0);
// Trace a ray
if (edgeFade > 0.02f)
ssr_hit_uv = SSFX_ssr_water_ray(Pv, wreflect, blue_noise, 0);
// Get current Skybox
refl_skybox = SSFX_calc_sky(vreflect) * G_SSR_WATER_SKY_REFLECTION;
if (all(ssr_hit_uv.xy))
{
// Get reflection pixel from scene screen
refl_ray = SSFX_get_image(ssr_hit_uv.xy, 0);
// Adjust reflection intensity using ssr_hit_uv.y and edgeFade
float ray_fade = ssr_hit_uv.y * 5.0f;
// Reflection fog fadeout
float fog = 1.0 - saturate(( length(float3(Pv.x,Pv.y,ssr_hit_uv.z)) * fog_params.w + fog_params.x) * 1.4f);
float refl_power = saturate(ray_fade * edgeFade * fog);
// Fallback to Skybox
reflection = lerp(refl_skybox * !ssfx_is_underground.x, refl_ray * G_SSR_WATER_MAP_REFLECTION, refl_power);
}
else
{
// No reflection data, we use only refl_skybox
reflection = refl_skybox * !ssfx_is_underground.x;
}
#else
reflection = L_hemi_color.rgb;
#endif
// Fresnel
#ifndef G_SSR_WATER_CHEAPFRESNEL
float fresnel = SSFX_calc_fresnel(eyedir, Nv, 1.33f);
float fresnel_amount = saturate(fresnel * 1.5);
#else
float fresnel = saturate (dot(vreflect, v2point));
float fresnel_amount = pow(fresnel, 3.0);
#endif
// Normal for the base texture...
float2 N_Tex = normalize(float3(basenormal.x * G_SSR_WATER_TEX_DISTORTION, basenormal.y * G_SSR_WATER_TEX_DISTORTION, basenormal.z));
// Get Position with Refraction discard
float3 _P2 = SSFX_get_position(PosTc + Refraction_UV, 0);
// 3d view space pos reconstruction math
_P2 = float3( _P2.z * ( I.hpos.xy * pos_decompression_params.zw - pos_decompression_params.xy ), _P2.z);
// Bottom of the water to world space ( Project the caustics and water fog )
float3 w_b = mul(m_inv_V, float4(_P2 , 1));
// Caustics offset anim
float4 CausticsT = float4( timers.x * 0.1f, 0.0f, -timers.x * 0.07f, 0.2f );
// Fake Caustics
float3 Ca0 = saturate(s_watercaustics.Sample(smp_base, w_b.xz * (0.19f / G_SSR_WATER_CAUSTICS_SCALE) + CausticsT.xy + N_Scr ) - 0.1f );
float3 Ca1 = saturate(s_watercaustics.Sample(smp_base, w_b.xz * (0.11f / G_SSR_WATER_CAUSTICS_SCALE) + CausticsT.zw + N_Scr ) - 0.1f );
float3 CA = min(Ca0, Ca1) * water_shadows * G_SSR_WATER_CAUSTICS; // Caustics if light hit the water
// Water Depth
float waterDepth = w_s.y - w_b.y + eye_position.y;
// Water Fog
float waterFog = exp(waterDepth) - 1.0f;
// Screen buffer
float3 screen = SSFX_get_image(PosTc + Refraction_UV, 0);
// Base texture * base color
float3 base_tex = s_base.Sample(smp_base, (w_s.xz + eye_position.xz) * 0.1f + N_Tex).rgb;
// Texture * base color
base_tex *= I.c0.rgb;
// Mix refraction ( 100% clear water ) & base texture ( Turbidity )
float3 turbidity = lerp(base_tex, screen, smoothstep(G_SSR_WATER_FOG_MAXDEPTH, -G_SSR_WATER_TURBIDITY, waterFog));
// Let's start the accumulation... First the water result and reflection.
float3 acc = lerp(turbidity, reflection, fresnel_amount * G_SSR_WATER_REFLECTION);
// Caustics. Fade through water fog
acc = acc + CA * smoothstep(G_SSR_WATER_FOG_MAXDEPTH + 0.5f, 0.0f, waterFog) * saturate(waterFog * 3.0f);
// Specular
#ifdef NEED_SPECULARS
float3 Nf = normalize(float3(Nw.x, Nw.y * G_SSR_WATER_SPECULAR_NORMAL, Nw.z)); // flatten the water normal to get better specular
float3 spec = L_sun_color.rgb * pow(abs(dot(normalize(v2point + L_sun_dir_w), Nf)), 512) * G_SSR_WATER_SPECULAR;
acc += spec * water_shadows;
#endif
// Fogging
#ifdef SSFX_FOG
float fogging = SSFX_FOGGING(1.0 - I.fog, w_s.y);
#else
float fogging = I.fog;
#endif
acc = lerp(fog_color, acc, fogging);
// Soft border
float border_alpha = smoothstep( 0.0 , G_SSR_WATER_SOFTBORDER, waterDepth);
// Done
return float4(acc, fogging * fogging * border_alpha);
}