226 lines
7.5 KiB
PostScript
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);
|
|
} |