User:VectorFluxion/Making Your Own Water
Water is one of the Source engine's most complex material types/shaders.
Water does not have a $basetexture
value by default, but rather, is made up of a real-time transforming normal map, fog, a combination of reflection and refraction, a flow map (for dx90 users and below), and an env_cubemap to give the water surface a specular reflection effect.
Water in the Source engine, is composed of three different elements:
- A material for when the observer is above the water surface, facing down into it;
- A material for when the observer is beneath the water surface, looking upwards; and,
- A scrolling overlay screen effect that the observer sees when underwater.
Contents
"Expensive" vs. "Cheap" Water
Cheap water has all the properties of expensive water; however, there are some notable differences.
- Cheap water is completely opaque from the surface, does not refract, and has limited reflection.
- Cheap water is commonly used at at distance, solely for optimization purposes.
- The entity that controls the distance in hammer units, that the expensive water begins fading to cheap water, is the water_lod_control.
Generic VMT Files For Water
The general code for the three elements of water.
For color values, both RGB and HSV can be used. Use square brackets for denote a HSV value "[H S V]"
and curly brackets to denote a RGB value "{R G B}"
.
For sub-dx90 users, a flow map (pictured to the right) must be used, as this will be transformed instead of a normal map. A flow map is also called a du/dv map, a derivative map, and a movement map. The flow map will be specified using the $bumpmap
material parameter. $bumpmap
should only be used in fallback materials, and specified in the main water file, using the <dx90
rule, with the $fallbackmaterial
material parameter inside.
It is generally recommended to make a fallback material for water, as water can be a memory-hog on older computers.
Above water material:
"Water" { "%compilewater" 1 "%keywords" "<string>" "%tooltexture" "<path> relative to <game abbr>/materials/" "$abovewater" 1 "$underwateroverlay" "<path> relative to <game abbr>/materials/" "$refracttexture" "_rt_WaterRefraction" "$refracttint" "<RGB>" or "<HSV>" "$normalmap" "<path> relative to <game abbr>/materials/" "$refractamount" <value> "$envmap" "env_cubemap" "$refractblur" 1 "$surfaceprop" water "$bottommaterial" "<path> relative to <game abbr>/materials/" "$scale" "[1 1]" "$bumpframe" 0 "$fogenable" <bool> "$fogcolor" "<RGB>" or "<HSV>" "$fogstart" <value in HU> "$fogend" <value in HU> "$temp" "[0 0]" "$curr" 0 "$curr2" 0 "Proxies" { "AnimatedTexture" { "animatedtexturevar" "$normalmap" "animatedtextureframenumvar" "$bumpframe" "animatedtextureframerate" 30 } "TextureTransform" { "translatevar" "$temp" "resultvar" "$bumptransform" } "Sine" { "sineperiod" 20 "sinemax" .6 "sinemin" -.6 "resultvar" "$curr" } "Sine" { "sineperiod" 15 "sinemax" -.6 "sinemin" .6 "resultvar" "$curr2" } "Equals" { "srcvar1" "$curr2" "resultvar" "$temp[0]" } "Equals" { "srcvar1" "$curr" "resultvar" "$temp[1]" } "WaterLOD" { "dummy" 0 } } }
Below water material:
"Water" { "%compilewater" 1 "%keywords" "<string>" "%tooltexture" "<path> relative to <game abbr>/materials/" "$abovewater" 0 "$cheapwaterstartdistance" <value in HU> "$cheapwaterenddistance" <value in HU> "$refractamount" <value> "$reflectamount" 1 "$refracttint" "<RGB>" or "<HSV>" "$reflecttint" "<RGB>" or "<HSV>" "$refracttexture" "_rt_WaterRefraction" "$scale" "[1 1]" "$blurrefract" 1 "$normalmap" "<path> relative to <game abbr>/materials/" "$underwateroverlay" "<path> relative to <game abbr>/materials/" "$surfaceprop" "water" "$bumpframe" 0 "$fogenable" <bool> "$fogcolor" "<RGB>" or "<HSV>" "$fogstart" <value in HU> "$fogend" <value in HU> "Proxies" { "AnimatedTexture" { "animatedtexturevar" "$normalmap" "animatedtextureframenumvar" "$bumpframe" "animatedtextureframerate" 30 } "TextureScroll" { "texturescrollvar" "$bumptransform" "texturescrollrate" .3 "texturescrollangle" 60 } } }
Screen distortion overlay effect:
"Refract" { "%keywords" "<string>" "%tooltexture" "<path> relative to <game abbr>/materials/" "$refractamount" <value> "$refracttint" "<RGB>" or "<HSV>" "$refractblur" <bool> "$bluramount" <0, 1, 2> "$ignorez" <bool> "$scale" "[1 1]" "$bumpmap" "<path> relative to <game abbr>/materials/" "$normalmap" "<path> relative to <game abbr>/materials/" "$bumpframe" 0 "Proxies" { "AnimatedTexture" { "animatedtexturevar" "$normalmap" "animatedtextureframenumvar" "$bumpframe" "animatedtextureframerate" 30 } "TextureScroll" { "texturescrollvar" "$bumptransform" "texturescrollrate" .2 "texturescrollangle" 45 } } }
Fallback Materials
Fallback materials are versions of existing materials that players will see if they are running older versions of DirectX. Fallback materials are invoked in the main material's VMT file, using a "fallback rule" and the $fallbackmaterial
material parameter. Because of its complex nature, it is generally recommended to make a fallback material for custom water materials. The code for the fallback material is roughly the same as the main material file; however, instead of using a normal map, a flow map is used. It is invoked using the $bumpmap
material parameter.
"Water" { ... <dx90 { "$fallbackmaterial" "<path> relative to <game abbr>/materials/" } }
The difference between bump maps and normal maps
There is a lot of confusion in determining the difference between a normal map and a bump map, moreover the difference between $bumpmap
and $normalmap
. Although the terms "normal map" and "bump map" are often incorrectly used interchangeably, they are in fact, different things.
A normal map is actually a type of bump map. $bumpmap
can be used to invoke a normal map in most cases; however, in cases like these where both $normalmap
and $bumpmap
are used, using the $bumpmap
parameter too liberally will cause problems with your material. In this case, we used $normalmap
to invoke the normal map, because $bumpmap
is used to invoke a flow map in fallback materials. A flow map is just another type of bump map. For LightmappedGeneric
textures, using either one will suffice. Think of the $normalmap
parameter as just a more specific version of $bumpmap.