Saturday, September 2, 2023

Basement Water

I've added a layer of water to the lowest levels of some of the office building backrooms basements. It's about a foot deep and mostly clear. The water is drawn using a physically accurate method that includes computing a Fresnel reflection term and calculating wavelength dependent absorption and scattering. Scattered light uses the color derived from nearby room ceiling lights and includes a shadow term. The top surface appears reflective at shallow view angles. The reflection code was reused from what I originally implemented for bathroom and dresser mirrors.

I've also added a system for tracking the 32 most recent splash effects and adding them as ripples that distort the water's surface. Each splash source generates sine waves that propagate out and are computed per-pixel in the fragment shader. This changes the normal used for lighting and also the texture coordinates used for reflection lookup. In addition, splashes add a local dirt/foam effect that temporarily makes the water a bit cloudy.

Everything that touches the water produces a splash with magnitude that depends on the force of the movement. This includes the player, people, and zombies walking through the water. It includes collisions of balls with the water when kicked or thrown. Pushing and dropping objects in the water generates splashes, as does opening and closing doors submerged in water.

This video shows my progress so far. I initially tried to get a good splash by dropping the fire extinguisher in the water, but it was difficult to see from that steep view angle. Ripples are easier to see when viewed at an angle from further away when the reflection term is stronger. The slight lag that occurs while the zombie is chasing me is due to the loading of a different 3D model for a zombie on the floor above me that never quite comes into view.


Here's an image of the same scene. The default is clear, clean water. The water absorbs the red component of the color more than the other wavelengths, and scatters the blue color the most. This results in the water appearing to have a blue tint that varies from light blue/white nearby to a deep blue in the distance where the camera's path through the water is longer due to the view ray slope. The absorption wavelengths can be adjusted in the shader to give the appearance of other fluids such as dirty/muddy water and even blood.

A basement full of clean, clear water that takes on a blue tint.

The same scene, but with dirty, muddy water. However, it has the same reflectivity, only the absorption has changed.

The same scene, now shown with bloody water. (Now that I took the screenshot I realize the foam should be dark red rather than white.)

The main() function of the fragment shader code can be found in my GitHub repo here.

I haven't yet figured out how to add refractions for the floors and walls under the water because these have already been drawn by the time the water is drawn over them. It's not particularly easy to customize the lighting and shading of these objects based on the water above them like I do with terrain. There are no caustic lighting patterns on the underwater objects either, for similar reasons. I have a caustic texture that I use when drawing the terrain under ocean water, but that same approach doesn't work well here. I'll look into a screen space approach that reads the current buffer contents before the water is drawn and includes that in the water shading, which may allow me to add these features.

Oh, and the water is currently only a flat plane with no height displacement due to waves. Also, the simulation is mostly run on the GPU, which doesn't have access to the wall locations. This means that waves propagate along the surface and through walls rather than reflecting off of them. I may be able to partially work around this by computing the bounds of each ripple by ray casting it against the walls on the CPU, then sending it to the GPU as a uniform buffer. This is how I handled room lights.

Finally, it would be nice to have an animated splash effect when objects enter the water. This would add some 3D volume to the water surface. I may be able to reuse the technique I used for raindrop splats in previous work.

I'll continue to think about these limitations to see if I can resolve them in the future.

5 comments:

  1. I think you later fixed the refractions, but it would be great to add some edge effects, both to the water and to the walls and other stationary objects.
    https://dryjersey.com/wp-content/uploads/elementor/thumbs/pic-17-q60fzrjtwha39usju8f3g4r7lql3j90u7bukti216w.png
    The water has a small bevel to it, which you could probably emulate by manipulating the normals. And surfaces that are wet get much darker, including a bit above the water line due to capillary action and ripple wetting.

    ReplyDelete
    Replies
    1. I fixed refractions last week. I also realized I wasn't handling blending properly where the water met the vertical walls and fixed that on Sunday.

      The place where water meets the wall isn't very visible to the player since you can't crouch, and flying down to the water level would likely get it clipped by the near clipping plane. I may be able to do something with this though by looking for very small values in the depth buffer. I'm not sure how to draw anything above the water level because the custom water shader is only run for pixels overlapped by the water plane.

      Delete
    2. Couldn't you make a custom value blend on all the material shaders in a flooded basement? Even a simple vertical gradient would be a big win for believably.

      Delete
    3. The basement materials (mostly concrete) are used in other areas of the building such as parking garages and part of the same draw call. It's not easy to apply a custom shader to them. It would probably be easier to draw a slightly higher water plane and add an effect for pixels between that plane and the regular water plane. I could possibly have some sort of 2D splash map to accumulate splashes and draw darker we areas higher up on walls near splashes. It would be a lot of effort though, so I'm not sure when I would get to it.

      Delete
    4. Yesterday I added a water surface to bathtubs, sinks, and showers when the water is turned on. It's not reflective or refractive though, and there are no waves or interactions. Just a partially transparent plane. I also added something similar to outdoor pools. I'm not sure if it's worth the trouble to add a complex water effect for small surfaces.

      Delete