User:VectorFluxion/Making Your Own Water

From Valve Developer Community
Jump to: navigation, search

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.

"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 flow map used by TF2's water, for sub-dx90 users.

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.

See Also