Roughness-responsive reflections for Single Layer Water

Hi,

Our project makes extensive use of roughness on Single Layer Water surfaces, and the current behavior where SLW reflections act as a polished mirror is producing undesired results for us, particularly for materials such as rough water or ice, where the reflection response is critical for the intended quality of the material.

We’ve been investigating ways to introduce some degree of roughness handling for SLW reflections, ideally with behavior closer to other opaque surfaces.

From looking through the SLW render path, it seems that the best place to address this would be during the SLW::LumenReflection step, before the reflection layer is composed on SLW::Composite (by the time the composite step runs, the reflection is already resolved and we don’t have an opportunity to apply any meaningful filtering)

I’m therefore considering whether roughness needs to be applied inside the tracing path used by RenderLumenReflections for ELumenReflectionPass::SingleLayerWater (for example in ReflectionTraceVoxelsCS/ the voxel tracing path) using the GBuffer’s roughness as an input to produce some pre-filtered reflection result before composition.

An alternative would be some form of pre-convolved reflection that could later be consumed by the composition step, similar in spirit to how the skylight is handled through CompositeReflectionCapturesAndSkylightTWS, though this starts to look like a more expensive solution at this point.

Before going too far with engine modifications, we wanted to ask:

  1. Does this sound like the correct area of the pipeline to investigate for rough SLW reflections?
  2. Perhaps I’m missing some engine-side reason why SLW reflections are forced to mirror behavior beyond the obvious quality/performance tradeoff?
  3. From a technical and performance standpoint, would you consider this a reasonable pursuit?

Additional note: we are not using Substrate materials

Thank you!!

[Attachment Removed]

Hi Dan!

I think this topic has come up before internally. IIRC the reason why we use mirror (Lumen) reflections on water is that with rough reflections we will need a more expensive denoising path on the water surface and it’s also possible that it’ll lead to bad smearing. The reason for that is that water needs to write motion vectors that somehow describe both the (apparent) motion of the water surface as well as the motion of the scene behind the water surface (in the case of fairly shallow and/or clear water). This is an active area of research and our current water motion vectors are probably not able to cleanly solve this.

That said, if you’re set on doing this, you’re looking in the right place: I would take a renderdoc capture and compare the passes Lumen runs on opaque geometry vs the passes it runs on water. There’s likely a toggle somewhere that forces reflections to zero roughness and it probably skips a bunch of denoising. In the end, SLW is simply calling into Lumen and handing it a bunch of rendertargets to calculate reflections for. It should all be fairly decoupled; so water doesn’t really “know” or “care” where the reflections come from, it just consumes them in the Composite pass.

If you achieve it, it will very likely cost you some additional GPU performance and it might also cost some (GPU) memory.

Cheers,

Tim

[Attachment Removed]

Thanks for the info and context, Tim! This is very helpful. If we decide to go ahead and end up with something useful, I’ll post it here!

Cheers,

Dan.

[Attachment Removed]