Valve Developer Union

Getting Around in the Material System

(February 4, 2018)


Though colloquially referred to as "textures", Source uses a much more customizable system for plastering images onto walls and characters. In the material system, a "texture" is actually a set of images and text files, which are used to define a surface's visual qualities, audible properties, and physical characteristics. This set is referred to as a material.

In this guide, we'll break materials down to their core to show you how it all works.

Parts of a material

At its most basic, two files are needed to get a material working in-game:

  1. The texture itself (referred to as an albedo), in VTF format
  2. A material file (VMT) defining its properties

VTFs (Valve Texture File) are created from any standard image format using Valve's pack-in command line application Vtex or using a third-party alternative, such as VTFEdit or VIDE. For the most part, VTFs are simply images encoded in a specialty format. They can't do much on their own, and the game will not recognize a loose VTF. To get it working, you'll need a VMT file to reference it.

brickfloor001a

The Valve Material File

The VMT (Valve MaTerial) file is what the game actually refers to when it loads a texture. It's simply a plain text file with a list of parameters used to load VTFs and define properties. Let's take a look at a very simple one before we get ahead of ourselves:

LightmappedGeneric
{
	$basetexture brick/brickfloor001a
	$surfaceprop brick
}

Let's break this down, line by line:

  1. Each VMT starts by referencing a shader. This is the bit of code the game will use to actually render the material. There's many different shaders meant for different uses, but the big two are LightmappedGeneric for brushes and VertexLitGeneric for models. This is important, as VertexLitGeneric materials can't be used on brushes, and LightmappedGeneric ones can't be used on models.
  2. Curly brackets ({}) are used to enclose shader parameters. Be sure these are closed, or the material will be considered malformed and won't work.
  3. $basetexture is an example of a shader parameter, defining a property of the material in question. In this case, this sets brick/brickfloor001a (right) as the albedo, or the texture to be rendered. Note that, if you have spaces in either the path or the name, you'll need to surround the path in quotes (e.g. $basetexture "path to texture/texture001").
  4. $surfaceprop is another shader parameter, one that'll define the decals the texture will use, what sound the surface makes when it's shot, and how the surface will reverberate sounds around it. This isn't necessarily needed, but it's good practice to include.

Now, provided you have both the material file and the VTF in materials/brick in the game files, it'll be usable in Hammer and show up in-game as a brick floor.

Customizing a material

Of course, this would all be rather silly if all a material did was display a texture. Additional files can be used to give visual depth and detail to a texture, and the material can be further customized with a variety of additional shader parameters. To add any of the following, simply type the parameter on its own line between the curly brackets.

Tip

Reload, Dr. Freeman

If you're making changes to a material, you can reload it without quitting out of the game using the command mat_reloadallmaterials in the console. All changes will be immediately visible after a short pause.

Adding depth with bumpmaps

An albedo next to a bumpmap
An albedo next to a bumpmap

Many shader parameters take a second texture that they'll use for a different purpose. Bumpmaps are one such example. A bumpmap is an image where each color (red, green, and blue) convey depth on one of the three axes. The game shades the surface using the information in the bumpmap; while the surface is still flat, the shading gives the illusion of depth.

cs_italy/marketwall05b with and without a bumpmap
cs_italy/marketwall05b with and without a bumpmap

The above wall, from cs_italy, shows a material with and without a bumpmap.

Bumpmaps can be generated by a variety of programs, including the aforementioned VTFEdit, so we won't go into how to make one. To add one to your material, use the $bumpmap parameter:

LightmappedGeneric
{
	$baseTexture cs_italy/marketwall05b
	%keywords cstrike
	$surfaceprop brick
	$bumpmap cs_italy/marketwall05b_normal
}

The brickfloor001a texture has been set up with a bumpmap on the left side of this wall. Notice the apparent depth in the texture compared to the version on the right.

An albedo next to a self-shadowing bumpmap
An albedo next to a self-shadowing bumpmap

Mappers using the Orange Box branch of the engine and newer also have the ability to use Valve's self-shadowing bumpmaps, which provide a better illusion of depth and are slightly faster to render. If you happen to have one of those, add it with the $bumpmap like earlier, but add an additional $ssbump parameter so the game knows to use that system instead:

WorldVertexTransition
{
	$basetexture nature/rockwall_cave01a
	$basetexture2 nature/dirtfloor004a
	$bumpmap Nature/rockwall_cave_02a_height-ssbump
	$ssbump 1
}
Inf

The du/dv Dilemma

There's actually a third type of depth texture, known as a du/dv map. This originally was used in Source for refractions, but nowadays, it's a deprecated format in the engine. Games using DirectX 8 and under will still fall back to using du/dv maps instead of bumpmaps, but according to Steam's hardware survey results for 2017, less than 1% of users are still using versions of DirectX that old, and that number continues to shrink. If you're creating custom materials, you can likely disregard du/dv maps entirely. Still, it's worth a mention here for posterity.

Adding detail with detail textures

Of course, blowing a texture up makes it look rather blurry. A detail texture is a smaller, greyscale texture applied on top of another to give the illusion of more detail than that texture actually has. This example, a plaster wall from dod_palermo, shows the difference a detail texture makes.

A wall with and without a detail texture
A wall with and without a detail texture

You can add it using the $detail parameter. This is commonly used in Valve's default materials as they're extremely cheap to render. (The following example is also an example of the $detailscale parameter, which defines the size at which the detail texture will be tiled at. In this case, it's just over seven times the original size of the detail texture.)

LightmappedGeneric
{
	$basetexture plaster/wallpaper002b
	$surfaceprop plaster
	%keywords c17downtown

	$detail detail\plaster_detail_01
	$detailscale 7.283
	$detailblendfactor .8
	$detailblendmode 0
}

Getting that squeaky-clean shine with cubemaps

Reflections and cubemaps are their own thing in Source, and if you're not familiar with how those work, check out "Cubemaps: What They Do, and How to Use Them" first. If you're looking to add them to a material, use the $envmap and $envmaptint parameters.

VertexLitGeneric
{
	<dx90
		{
			$fallbackmaterial models/props_gameplay/cap_point_base_dx80
		}

	$basetexture Models/props_gameplay/cap_point_base
	$bumpmap Models/props_gameplay/cap_point_base_normal
	
	$envmap env_cubemap
	$normalmapalphaenvmapmask 1 
	$envmaptint "[1 1 1]"
}
Reflections on a control point in cp_granary created by a cubemap
Reflections on a control point in cp_granary created by a cubemap

$envmap is a special parameter, because you can either specify a custom environment map texture or a generic env_cubemap if you want it to use the nearest cubemap entity when the map is compiled. $envmaptint controls the intensity of the reflection, using a vector value containing three numbers between 0 and 1, one each for the red, green, and blue channels. (The vector needs to be wrapped in quotes, as it uses spaces.) The above example, a control point from cp_granary, has a pure white reflection, "[1 1 1]". If you need the reflection fainter, set all three numbers lower.

These are just a few of the ways you can customize a material. There's loads more shaders and parameters if you need to get specific effects on a texture, even some specific to the game you're mapping for. You can even do light scripting on a material through the material proxy system, which is heavy enough to require its own guide.