Making Use of Source's Water Materials
(December 3, 2017)
The Source engine had some of the most detailed and lifelike water effects for its day, and even to this day, the effect holds up well. Water in Source is reflective, refractive, fogged, and has the ability to flow if needed. With all of this comes restrictions on how it can be placed and the potential for glitches if used improperly.
Before we get into any specifics, know that there's actually two kinds of water materials, cheap water and expensive water.
Huh? Cheap and expensive water?
Rendering realistic water takes time. This is generally what we refer to when we call something "cheap" or "expensive" to render. Expensive water is a multi-step rendering process, while cheap water often uses a single, tinted texture. Here's a list of the different rendering passes that go into an expensive water material.
- The water is refracted, or the area underneath it is visually distorted, using a bumpmap or a du/dv map.
- The water reflects, or mirrors the area above it, either using real-time reflections or using a cubemap. This is also distorted using the bump map.
- The water is fogged to color it, and the fog color and thickness can be set in the material's parameters.
- Sprites are usually added to the water to simulate dirt and other floating debris.
This is a demonstration of each rendering pass of an expensive water material. In order from left to right, we have a water material using only a bump map, a water material using a bump map and real-time refraction, a water material using a bump map and real-time reflection, and a water material using all three.
In addition to all of this, expensive water has restrictions on how and where it can be used. It takes time to render expensive water, time you may not have in a complicated scene.
Cheap water, in comparison, oftentimes only features fog and uses a normal $basetexture
like other brushes, with a cubemap if necessary for reflections. It features no fogging, refraction, or real-time reflections. Cheap water also features fewer restrictions on its use than expensive water.
Adding water to a map
To add any kind of water to a map, draw out a brush textured with tools/toolsnodraw
and texture the top with the water material of your choice. The water brush can intersect other brushes, but it has to be rectangular in shape. When the map is compiled, the compiler will turn it into a transparent brush that the player will be able to swim through. Note that water will not seal a map or block VVIS from seeing through it.
Different games will have different water textures, and there's no way we could fit a listing into one guide. Instead, we direct your attention to two separate videos. one video compares the water textures featured in Counter-Strike: Source and shared with Half-Life 2, Day of Defeat: Source, and other games of its era. The other compares the water textures available for Counter-Strike: Global Offensive mappers.
Restrictions on water brushes
As stated earlier, the water shader has several restrictions on how expensive water is used in a map. Flickering and other visual inconsistencies can occur if proper care isn't taken.
- There can't be two different water levels in a scene, or more technically, the same PVS, if the water brushes use an expensive material.
- The water brush can't slope on any side or be any shape other than rectangular. This is especially necessary on the top and bottom of the water brush. Water brushes can extend outside the perimeter of their container (say, for pipes) if necessary.
- Areaportals can't cross the water's surface. If you have a
func_areaportal
that does, split it in half and make the results of the split two separate areaportal entities. - The water brush can't be tied to an entity.
func_water_analog
, which renders moving water, is the sole exception. - You can't mix the two types of water in the same scene (or the same PVS).
- The water texture should only be used on the top of the brush. The other sides should be
tools/toolsnodraw
. - Water won't render properly in the case of a map leak. Water also won't seal the map.
- A
water_lod_control
entity is needed to control the distance at which expensive water will transition into cheap water. VBSP will make one for you if one isn't present in the map. - Since expensive water can turn into cheap water through the player's video settings or through distance from the player, an
env_cubemap
on the surface of the water is needed to render static reflections.
If you find yourself in a position where you're unable to use an expensive, realistic water material, you can fall back to using any water material with _dx70
or _cheap
in the name. While it won't look as nice, it's more functional, and depending on the map, the loss of detail likely won't be an issue. In the above screenshot from Half-Life: Source, the use of cheap water barely sticks out.