Hello,
While developing custom water materials together with landscape terrain, we observed that under certain view angles and shallow water depths the water surface exhibits regular banding artifacts. Initially we suspected this was caused by modifications in our rendering pipeline. However, after simplifying the setup and reproducing the issue, I was also able to observe the same behavior in vanilla UE 5.7. [Image Removed]
This artifact appears most frequently in very shallow water regions, especially around the shoreline.
After performing shader debugging, the issue seems to originate from the following code inside:
WaterVolumeLightingOutput EvaluateWaterVolumeLighting()
float4 SceneDeviceZ4 = SceneDepthWithoutSingleLayerWaterTexture.Gather(SceneDepthWithoutSingleLayerWaterSampler, DistortedUV);
float4 SceneDepth4 = float4(
ConvertFromDeviceZ(SceneDeviceZ4.x),
ConvertFromDeviceZ(SceneDeviceZ4.y),
ConvertFromDeviceZ(SceneDeviceZ4.z),
ConvertFromDeviceZ(SceneDeviceZ4.w)
);
float SceneDepth = 0.0f;
float SceneDeviceZ = 0.0f;
if (any(SceneDepth4 < WaterDepth))
{
SceneDepth = PixelSceneDepth; // The pixel we try to sample is closer than the water pixel: fallback to no distortion
SceneDeviceZ = PixelSceneDeviceZ;
}
else
{
// We can use bilinear interpolation here because all depth samples are guaranteed to be farther away than the water surface depth.
SceneDeviceZ = GetBilinearInterpolation(
GetBilinearSampleLevelInfos(
DistortedUV,
SceneDepthWithoutSingleLayerWaterTextureSize,
SceneDepthWithoutSingleLayerWaterTextureInvSize),
SceneDeviceZ4.wzxy);
SceneDepth = ConvertFromDeviceZ(SceneDeviceZ);
ViewportUV = DistortedUV;
}
The artifact appears when execution enters the else branch and calls GetBilinearInterpolation. In those shallow-water pixels the banding becomes visible.
My current hypothesis is that SceneDeviceZ4 is obtained using a Gather operation, while GetBilinearSampleLevelInfos() reconstructs a bilinear sample based on DistortedUV. These two operations may not necessarily refer to the exact same pixel footprint, which could introduce subtle discontinuities when interpolating the gathered samples.
As an experiment, I replaced the bilinear interpolation path with a direct texture sample:
SceneDeviceZ = SceneDepthWithoutSingleLayerWaterTexture.SampleLevel(
SceneDepthWithoutSingleLayerWaterSampler,
DistortedUV,
0
).r;
With this change, the banding artifacts disappear.
Reproduction steps
- Create a simple SingleLayerWater material as follow setup.
- Create a very shallow landscape with a Lake WaterBody.
- Observe the shoreline region where water meets terrain.
- Under certain camera angles, the water surface shows regular banding patterns.
[Image Removed]
As a temporary workaround on our side, we increased the water depth bias to avoid rendering extremely shallow water regions, which reduces the visible artifacts.
However, we would like to ask:
- Is this the expected behavior of this sampling path?
- Is there a recommended or more correct way to handle this situation for shallow water?
Any guidance would be greatly appreciated.
Thank you!
[Attachment Removed]