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

153 lines
4.5 KiB
PostScript
Raw Normal View History

2024-03-17 20:18:03 -04:00
/**
* @ Version: SCREEN SPACE SHADERS - UPDATE 14.6
* @ Description: SSDO implementation
* @ Modified time: 2023-02-07 00:11
* @ Author: https://www.moddb.com/members/ascii1457
* @ Mod: https://www.moddb.com/mods/stalker-anomaly/addons/screen-space-shaders
*/
#ifndef SSFX_READY
#include "screenspace_common.h"
#endif
#include "settings_screenspace_AO.h"
#ifndef SSAO_QUALITY
float calc_ssdo (float3 P, float3 N, float2 tc, float4 pos2d, uint iSample)
{
return 1.0;
}
#else // SSAO_QUALITY
// Internal values
#if SSAO_QUALITY == 3
#define G_SSDO_SAMPLE 32
#elif SSAO_QUALITY == 2
#define G_SSDO_SAMPLE 16
#elif SSAO_QUALITY == 1
#define G_SSDO_SAMPLE 8
#endif
float calc_ssdo (float4 P, float3 N, float2 tc, float4 pos2d, uint iSample)
{
// Rendering AO till G_SSDO_RENDER_DIST
if (P.z >= G_SSDO_RENDER_DIST || P.z <= SKY_EPS)
return 1;
// Ao Noise
float ao_noise = frac(sin(dot(tc, float2(12.0, 78.0))) * 43758.0) * clamp(P.z * 0.05f, G_SSDO_NOISE_MIN, G_SSDO_NOISE_MAX);
// MAT_FLORA from current pixel?
bool mat_flora = abs(P.w - MAT_FLORA) <= 0.04f;
// If MAT_FLORA we use a fake normal to align the hemisphere ( We can't trust much about flora normal )
N.xyz = mat_flora ? float3(0.0f, 0.25f, 0.0f) : N.xyz;
// A factor to adjust some values to only affect weapons ( closer objects )
float WeaponFactor = 1.0f - smoothstep(G_SSDO_WEAPON_LENGTH * 0.5f, G_SSDO_WEAPON_LENGTH, length(P.xyz));
// Var to accumulate the AO
float occ = 0;
// AO Radius
float radius = G_SSDO_RADIUS;
// Radius weapon adjustements
radius *= lerp(1.0f, G_SSDO_WEAPON_RADIUS * clamp(P.z * 2.0f, 0.2f, 1.0f), WeaponFactor);
#ifdef G_SSDO_DETAILED_RADIUS
float detail_radius = radius * lerp(G_SSDO_DETAILED_RADIUS, G_SSDO_DETAILED_WEAPON_RADIUS, WeaponFactor);
#endif
[unroll (G_SSDO_SAMPLE)]
for (int i = 0; i < G_SSDO_SAMPLE; i++)
{
// Use surface normal and add the hemisphere distribution
float3 sample_rays = (ssfx_hemisphere[i] + N.xyz) * (1.0f + ao_noise);
// Sample position
float3 occ_pos = P.xyz + sample_rays * radius;
// Sample position to UV
float2 occ_pos_uv = SSFX_view_to_uv(occ_pos);
// Process valid UV coor
if (SSFX_is_valid_uv(occ_pos_uv))
{
// Sample depth buffer
float4 sample_pos = SSFX_get_position(occ_pos_uv, iSample);
// AO Distance attenuation. To discard incorrect AO and add some attenuation through distance.
float atte = lerp( 0.1f, 4.0f, smoothstep(G_SSDO_WEAPON_LENGTH * 0.5f, G_SSDO_WEAPON_LENGTH, sample_pos.z));
// Pixel occluded?
float AO = step(sample_pos.z, occ_pos.z);
// Detailed Search if there's no AO
#ifdef G_SSDO_DETAILED_SEARCH
if (AO < 1)
{
// Same has before but with smaller radius
occ_pos = P.xyz + sample_rays * detail_radius;
occ_pos_uv = SSFX_view_to_uv(occ_pos); // We don't check for a valid UV coor, is already done and this sample radius is smaller ( in theory )
sample_pos = SSFX_get_position(occ_pos_uv, iSample); // Get Sample
AO = step(sample_pos.z, occ_pos.z); // Check occlusion
atte *= saturate(detail_radius + WeaponFactor); // Adjust attenuation for the scenary AO
}
#endif
// Distance between pixel and "occluder"
float3 Dist = distance(float3(occ_pos_uv.x, occ_pos_uv.y, sample_pos.z), float3(tc.x, tc.y, P.z));
// Difference attenuation
AO *= 1.0 - smoothstep(0.0f, atte, Dist );
// MAT_FLORA from sample?
bool sample_mat_flora = abs(gbuf_unpack_mtl(sample_pos.w) - MAT_FLORA) <= 0.04f || mat_flora;
// Value for dot product for full intensity ( Adjust value for WeaponFactor, we want a extremely sensitive AO for weapons )
float N_a = saturate( WeaponFactor ) * 0.8f;
// Dot product using current normal and sampled normal to adjust intensity ( Change mode if MAT_FLORA )
if (!sample_mat_flora)
AO *= smoothstep(1.0f, N_a, dot(gbuf_unpack_normal( sample_pos.xy ), N ));
else
AO *= G_SSDO_FLORA_INTENSITY;
// Discard incorrect occlusion from the sky.
AO *= !is_sky(sample_pos.z);
// Accumulate final AO
occ += AO;
}
else
{
// Try next step with less radius
radius *= 0.75f;
}
}
// Normalize AO
occ /= G_SSDO_SAMPLE;
// Invert AO
occ = 1.0f - occ;
// AO intensity
occ = pow(occ, G_SSDO_INTENSITY * (1.0f - (1.0f - G_SSDO_WEAPON_INTENSITY) * WeaponFactor));
// AO softer
occ = lerp(occ, sqrt(occ), G_SSDO_SMOOTH);
// Limit AO maximum occlusion
occ = clamp(occ, G_SSDO_MAX_OCCLUSION, 1.0f);
// Fadeout using G_SSDO_RENDER_DIST
occ += smoothstep(G_SSDO_RENDER_DIST * 0.8f, G_SSDO_RENDER_DIST, P.z);
// Return AO
return saturate(occ);
}
#endif // SSAO_QUALITY