Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
66a0a97
feat: add raindrop ripples on water
TheRiverwoodModder Sep 30, 2024
2a08276
style: 🎨 apply clang-format changes
TheRiverwoodModder Sep 30, 2024
a2d6666
feat: ripple effect now supports water parallax
TheRiverwoodModder Oct 6, 2024
a7ffdde
Merge branch 'dev-optimized' of https://github.com/TheRiverwoodModder…
TheRiverwoodModder Oct 6, 2024
61eca33
style: 🎨 apply clang-format changes
TheRiverwoodModder Oct 6, 2024
4450145
Merge branch 'dev' into dev-optimized
alandtse May 24, 2025
e720469
Merge branch 'dev' of https://github.com/doodlum/skyrim-community-sha…
alandtse Jun 8, 2025
1a5c8d4
fix: fix compilation errors
alandtse Jun 8, 2025
4be0dc8
fix: fix wrong type
alandtse Jun 8, 2025
e6e510b
chore: sync cache on defaults
alandtse Jun 8, 2025
f1122aa
style: 🎨 apply pre-commit.ci formatting
pre-commit-ci[bot] Jun 8, 2025
467e4fe
fix: vanilla ripple display
alandtse Jun 8, 2025
60af9ea
refactor: add flowmap functions
alandtse Jun 13, 2025
f913c3b
feat: add flow maps to ripples
alandtse Jun 13, 2025
ac49e5d
style: 🎨 apply pre-commit.ci formatting
pre-commit-ci[bot] Jun 13, 2025
e724017
fix: fix ripple and splash flows
alandtse Jun 16, 2025
0f0dd6e
Merge branch 'dev' of https://github.com/doodlum/skyrim-community-sha…
alandtse Jun 16, 2025
41912b1
fix: remove dupe GetFeatureModLink
alandtse Jun 17, 2025
215e272
style: fix lint
alandtse Jun 17, 2025
90ffd50
refactor: address further ai comments
alandtse Jun 17, 2025
c8407da
fix: avoid entropy collapse in ripple hash
alandtse Jun 17, 2025
6f21811
style: restore function names and lines
alandtse Jun 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 128 additions & 4 deletions features/Wetness Effects/Shaders/WetnessEffects/WetnessEffects.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ namespace WetnessEffects
}

// xyz - ripple normal, w - splotches
float4 GetRainDrops(float3 worldPos, float t, float3 normal)
float4 GetRainDrops(float3 worldPos, float t, float3 normal, float rippleStrengthModifier = 1.0, float2 flowOffset = float2(0.0, 0.0))
{
// Apply flow offset to world position for flow-aware ripple positioning
worldPos.xy += flowOffset;

Comment thread
alandtse marked this conversation as resolved.
const static float uintToFloat = rcp(4294967295.0);
const float rippleBreadthRcp = rcp(SharedData::wetnessEffectsSettings.RippleBreadth);

Expand Down Expand Up @@ -106,13 +109,18 @@ namespace WetnessEffects
float distSqr = dot(vec2Centre, vec2Centre);
float rippleT = residual * SharedData::wetnessEffectsSettings.RippleLifetimeRcp;
if (rippleT < 1.) {
float ripple_r = lerp(0., SharedData::wetnessEffectsSettings.RippleRadius, rippleT);
// vary ripple size using high-quality random hash (preserves full entropy)
uint sizeHash = Random::iqint3(hash.xy);
float sizeRandom = float(sizeHash) * uintToFloat;
float sizeVariation = lerp(0.7, 1.3, sizeRandom);

float ripple_r = lerp(0.f, SharedData::wetnessEffectsSettings.RippleRadius * sizeVariation, rippleT);
float ripple_inner_radius = ripple_r - SharedData::wetnessEffectsSettings.RippleBreadth;

float band_lerp = (sqrt(distSqr) - ripple_inner_radius) * rippleBreadthRcp;
if (band_lerp > 0. && band_lerp < 1.) {
float deriv = (band_lerp < .5 ? SmoothstepDeriv(band_lerp * 2.) : -SmoothstepDeriv(2. - band_lerp * 2.)) *
lerp(SharedData::wetnessEffectsSettings.RippleStrength, 0, rippleT * rippleT);
lerp(SharedData::wetnessEffectsSettings.RippleStrength * rippleStrengthModifier, 0, rippleT * rippleT);

float3 grad = float3(normalize(vec2Centre), -deriv);
float3 bitangent = float3(-grad.y, grad.x, 0);
Expand All @@ -137,7 +145,7 @@ namespace WetnessEffects
float3 R = reflect(-V, N);
float NoV = saturate(dot(N, V));

#if defined(DYNAMIC_CUBEMAPS)
#if defined(DYNAMIC_CUBEMAPS) && !defined(WATER)
# if defined(DEFERRED)
float level = roughness * 7.0;
float3 specularIrradiance = 1.0;
Expand Down Expand Up @@ -168,4 +176,120 @@ namespace WetnessEffects
{
return LightingFuncGGX_OPT3(N, V, L, roughness, 0.02) * lightColor;
}

// Debug visualization functions for DEBUG_WETNESS_EFFECTS
#ifdef DEBUG_WETNESS_EFFECTS
/**
* Calculates ripple and splash effect intensities from water ripple info
*
* @param rippleInfo float4 containing scaled ripple normal (xyz) and splash intensity (w)
* Note: xyz = normalized ripple normal * intensity multiplier
* @param rippleMultiplier Multiplier for ripple effect intensity
* @param splashMultiplier Multiplier for splash effect intensity
* @return float2 where x=ripple effect, y=splash effect
*/
float2 GetDebugEffectIntensities(float4 rippleInfo, float rippleMultiplier, float splashMultiplier)
{
// rippleInfo.xyz is a scaled normal vector (normalized normal * intensity)
// length() gives us the intensity/magnitude of the ripple effect
float rippleEffect = saturate(length(rippleInfo.xyz) * rippleMultiplier);
float splashEffect = saturate(rippleInfo.w * splashMultiplier);
return float2(rippleEffect, splashEffect);
}

/**
* Generates debug color visualization for wetness effects
*
* @param effectIntensities float2 from GetDebugEffectIntensities()
* @param rippleColor Color to use for ripple visualization
* @param splashColor Color to use for splash visualization
* @param baseColor Base color to start with (default black)
* @param brightnessMultiplier Multiplier for effect brightness
* @return float3 Debug color, or (0,0,0) if no effects are active
*/
float3 GetDebugWetnessColor(float2 effectIntensities, float3 rippleColor, float3 splashColor, float3 baseColor = float3(0, 0, 0), float brightnessMultiplier = 1.0)
{
float rippleEffect = effectIntensities.x;
float splashEffect = effectIntensities.y;

if (rippleEffect > 0.01 || splashEffect > 0.01) {
float3 debugColor = baseColor;
if (rippleEffect > 0.01) {
debugColor += rippleColor * rippleEffect * brightnessMultiplier;
}
if (splashEffect > 0.01) {
debugColor += splashColor * splashEffect * brightnessMultiplier;
}
return saturate(debugColor);
}
return float3(0, 0, 0); // No debug override
}

/**
* Convenience function for standard water debug colors
*/
float3 GetDebugWetnessColorStandard(float4 rippleInfo, float rippleMultiplier, float splashMultiplier)
{
float2 effects = GetDebugEffectIntensities(rippleInfo, rippleMultiplier, splashMultiplier);
float3 rippleColor = float3(1.0, 0.0, 1.0); // BRIGHT MAGENTA
float3 splashColor = float3(0.0, 1.0, 0.0); // BRIGHT GREEN
return GetDebugWetnessColor(effects, rippleColor, splashColor);
}

/**
* Convenience function for specular debug colors (extra bright)
*/
float3 GetDebugWetnessColorSpecular(float4 rippleInfo, float rippleMultiplier, float splashMultiplier)
{
float2 effects = GetDebugEffectIntensities(rippleInfo, rippleMultiplier, splashMultiplier);
float3 rippleColor = float3(1.0, 0.0, 1.0); // BRIGHT MAGENTA
float3 splashColor = float3(0.0, 1.0, 0.0); // BRIGHT GREEN
return GetDebugWetnessColor(effects, rippleColor, splashColor, float3(0, 0, 0), 1.5); // Extra bright
}

/**
* Convenience function for underwater debug colors (darker)
*/
float3 GetDebugWetnessColorUnderwater(float4 rippleInfo, float rippleMultiplier, float splashMultiplier)
{
float2 effects = GetDebugEffectIntensities(rippleInfo, rippleMultiplier, splashMultiplier);
float3 rippleColor = float3(0.7, 0.0, 0.7); // DARK MAGENTA
float3 splashColor = float3(0.0, 0.7, 0.0); // DARK GREEN
return GetDebugWetnessColor(effects, rippleColor, splashColor, float3(0, 0, 0.2)); // Dark blue base
}
#endif

/**
* Calculates flow-aware ripple positioning with proper timing synchronization
*
* @param worldFlowVector Flow vector in world coordinate space
* @param flowStrength Flow strength (0-1) from flowmap alpha channel
* @param reflectionTimingScale Timing scale factor (typically 0.001 * ReflectionColor.w)
* @param avgFlowmapMultiplier Average multiplier from flowmap normal calculations
* @param uvToWorldScale Scale factor converting UV coordinates to world positioning (typically 1/8)
* @return float2 Flow offset to apply to ripple positioning
*
* @details This function synchronizes ripple movement timing with flowmap normal animations
* by using the same mathematical relationship and dual-phase smoothstep timing.
* The timing creates natural flow-based ripple movement that matches the water surface animation.
*/
float2 GetFlowAwareRippleOffset(float2 worldFlowVector, float flowStrength, float reflectionTimingScale, float avgFlowmapMultiplier = 9.26, float uvToWorldScale = 0.125)
{
// Calculate flow timing scale matching flowmap normal timing
// Mathematical relationship: avgMultiplier × uvToWorldScale gives base flow scaling
// uvToWorldScale (1/8) relates to the 64× texture coordinate scaling: 64 × (1/8) = 8
float baseFlowMultiplier = avgFlowmapMultiplier * uvToWorldScale; // ≈ 1.16
float flowTimeScale = baseFlowMultiplier * reflectionTimingScale; // Match flowmap timing

// Calculate base flow offset (strength-modulated)
float2 flowOffset = worldFlowVector * flowTimeScale * flowStrength;

// Apply dual-phase smoothstep timing for natural flow animation
// This creates the essential dual-phase animation pattern used in flowmap blending
float smoothTime = smoothstep(0.0, 1.0, frac(flowTimeScale));
smoothTime = 0.15 + 0.85 * smoothTime; // Range: 0.15→1.0→0.15 (avoids complete stops)

return flowOffset * smoothTime;
}

}
11 changes: 6 additions & 5 deletions package/Shaders/Common/SharedData.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,23 @@ namespace SharedData

bool EnableSplashes;
bool EnableRipples;
uint EnableVanillaRipples;
Comment thread
alandtse marked this conversation as resolved.
float RaindropFxRange;

float RaindropGridSizeRcp;
float RaindropIntervalRcp;

float RaindropChance;
float SplashesLifetime;

float SplashesStrength;
float SplashesMinRadius;

float SplashesMaxRadius;
float RippleStrength;

float RippleRadius;
float RippleBreadth;

float RippleLifetimeRcp;

float3 pad0;
float pad0;
};

struct SkylightingSettings
Expand Down
Loading