diff --git a/features/Upscaling/Shaders/Upscaling/UnderwaterMaskUpscalePS.hlsl b/features/Upscaling/Shaders/Upscaling/UnderwaterMaskUpscalePS.hlsl index 66dfd559be..213ccd6931 100644 --- a/features/Upscaling/Shaders/Upscaling/UnderwaterMaskUpscalePS.hlsl +++ b/features/Upscaling/Shaders/Upscaling/UnderwaterMaskUpscalePS.hlsl @@ -63,6 +63,15 @@ PS_OUTPUT main(PS_INPUT input) // itself, which always maps to the center tile (12). float waterHeight = SharedData::GetWaterData(float3(0, 0, 0), eyeIndex).w; + // Tile sentinel: try TESWaterSystem fallback. WaterSystemHeight is valid only when + // playerUnderwater == true (fully submerged); it is stored eye-0 camera-relative so + // the same per-eye correction as GetWaterData applies. + if (waterHeight <= WATER_HEIGHT_NO_TILE_SENTINEL) { + float sysHeight = SharedData::WaterSystemHeight; + if (sysHeight > WATER_HEIGHT_NO_TILE_SENTINEL) + waterHeight = sysHeight + FrameBuffer::CameraPosAdjust[0].z - FrameBuffer::CameraPosAdjust[eyeIndex].z; + } + // GetWaterData returns INT_MIN (~-2.147e9) when the tile is outside the 5x5 grid. if (waterHeight > WATER_HEIGHT_NO_TILE_SENTINEL) { // Unpack from side-by-side stereo layout to per-eye UV [0, 1] @@ -112,9 +121,10 @@ PS_OUTPUT main(PS_INPUT input) } return psout; } - // No water tile in range: fall through to the standard sampler path. + // No water tile or system height available: fall through to the standard sampler path. // The left-eye result from the vanilla mask is still accurate here; the right-eye - // will be approximate, but in the absence of nearby water the visual impact is nil. + // will be approximate, but both sources failing implies no nearby water so the + // visual impact is nil. # endif // Upscale using linear sampling with jitter-corrected coordinates diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 767068f6df..ed40111591 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -20,11 +20,11 @@ namespace SharedData float Timer; uint FrameCount; uint FrameCountAlwaysActive; - bool InInterior; // If the area lacks a directional shadow light e.g. the sun or moon - bool InMapMenu; // If the world/local map is open (note that the renderer is still deferred here) - bool HideSky; // HideSky flag in WorldSpace, e.g. Blackreach - float MipBias; // Offset to mip level for TAA sharpness - float pad0; + bool InInterior; // If the area lacks a directional shadow light e.g. the sun or moon + bool InMapMenu; // If the world/local map is open (note that the renderer is still deferred here) + bool HideSky; // HideSky flag in WorldSpace, e.g. Blackreach + float MipBias; // Offset to mip level for TAA sharpness + float WaterSystemHeight; // TES::GetWaterHeight at eye-0 in camera-relative Z; -FLT_MAX when no water body found (VR only) float4 AmbientSHR; float4 AmbientSHG; float4 AmbientSHB; diff --git a/src/State.cpp b/src/State.cpp index 4892da9424..04db5dd2ae 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -856,6 +856,24 @@ void State::UpdateSharedData([[maybe_unused]] bool a_inWorld, [[maybe_unused]] b } } + // Fallback water height for the VR analytical mask when tile 12 returns the sentinel. + // Uses player->GetWaterHeight() (reads relevantWaterHeight from LOADED_REF_DATA) gated by + // underwaterCount > 0 so it is only set when the player is actually in a water body. + // Covers both interior water (where TES::GetWaterHeight returns -NI_INFINITY) and exterior + // partial submersion. Stored as eye-0 camera-relative Z to match WaterData[].w. + data.WaterSystemHeight = -RE::NI_INFINITY; + if (globals::game::isVR) { + if (auto player = globals::game::player) { + if (player->loadedData && player->loadedData->underwaterCount > 0) { + float worldHeight = player->GetWaterHeight(); + if (worldHeight > -RE::NI_INFINITY) { + auto eye0Pos = Util::GetEyePosition(0); + data.WaterSystemHeight = worldHeight - eye0Pos.z; + } + } + } + } + data.InInterior = Util::IsInterior(); if (globals::game::sky) diff --git a/src/State.h b/src/State.h index 3d9ae141ec..ad2658888b 100644 --- a/src/State.h +++ b/src/State.h @@ -218,7 +218,7 @@ class State uint InMapMenu; uint HideSky; float MipBias; - float pad0; // unused; must match SharedData.hlsli cbuffer (AmbientSHR 16-byte alignment) + float WaterSystemHeight; // TES::GetWaterHeight at eye-0 in camera-relative Z; -NI_INFINITY when no water body found (VR only) float4 AmbientSHR; float4 AmbientSHG; float4 AmbientSHB; diff --git a/src/Utils/Game.cpp b/src/Utils/Game.cpp index 31900f417c..464bc863d0 100644 --- a/src/Utils/Game.cpp +++ b/src/Utils/Game.cpp @@ -84,7 +84,9 @@ namespace Util data.z *= color.blue; } - data.w = cell->GetExteriorWaterHeight() - position.z; + float cellWaterHeight; + if (cell->GetWaterHeight(position, cellWaterHeight)) + data.w = cellWaterHeight - position.z; return data; }