Adapting PBR Textures to Source

From Valve Developer Community
Jump to: navigation, search
 English 简体中文 
Not to be confused with Adding PBR to your mod.

Physically based rendering (PBR) textures and their associated workflow have become the latest industrial standard over the years. The Source engine, predating that change, doesn't ship with them by default, and thus is barred from the newer, first-class texture arts. While there have been attempts to change Source to fit PBR, what's really helpful for non-modding mappers is to change PBR to fit Source. This tutorial is such an attempt.

Currently, no formal studies are known to be available on the internet, but a conversion formula can still be constructed in an empirical way regardless.

The tutorial focuses on brush materials, thus skipping the more complex Phong map. Make sure you have an image processing program to perform the conversion.


First of all, it's helpful to figure out what Source and PBR do and the difference between them. Essentially, the traditional method, including Source's, is about the resulted rendering; PBR textures on the other hand is about the physics of the material, and the resulted rendering is predictable with human sense.

In specific, Source materials typically contain the following layers:

Sometimes an AO (ambient occlusion) map through SSBump map is used. For simplicity this article will ignore it and focus on the basic three layers.

The logic of the three layers is simple. The total reflectivity of a surface is decomposed into two types: diffuse and specular, represented by the albedo and specular map, respectively, with the local bumping represented by the normal map.

PBR texture arts vary depending on providers. Apparently each texturist has their own approach, but a typical setting for non-metal textures include:

  • Albedo (also called base color or diffuse map)
  • AO map
  • Displacement map (also called heightmap)
  • Roughness map (or glossiness/smoothness map)
  • Normal map

For textures with metalness, an additional type of map, either specular map or metalness map, is used. The key is to understand what effects each map has on diffuse and specular reflectivity so that they can be simulated in Source.


For non-metallic materials, all we need is to construct a series of functions to convert the five maps of PBR to the three maps of Source.

Normal Map

The normal mapping technique hasn't changed in the slightest all these years, so it may be taken to Source as-is. It still varies case by case whether it should have its green channel inverted; that has nothing to do with PBR, though.

normalSource = normalPBR

Displacement Map

This shows the displacement or height of the surface. The information should have been addressed by the normal map, so it's probably alright to drop it. Alternatively, especially when AO isn't present, the displacement map may be baked into the albedo or the blue channel of the normal map. Let it screen, multiply, or overlay the other layer with opacity no more than 25%.

If that loose advice doesn't make sense to you, just try:

albedoSource = multiply ((12.5%) displacementPBR, albedoPBR)


This determines the reflectivity of ambient light. Not supported for brushes in Source, it has to be baked into the albedo. Supposedly it should multiply the albedo, but with high opacity the result can be gloomy. An opacity of 25% is a good starting point.

albedoSource = multiply ((25%) AOPBR, albedoSource)

An alternative way is to level it up before blending, as shown in picture.

Leveling the AO of a PBR texture before baking it into the albedo

Try letting it multiply the albedo with about 50% opacity.

Roughness Map

The roughness map is a tricky one because it has to do with both diffuse and specular reflectivity. A rough surface reflects less specular light compensated by more diffuse light, and vice versa. The law of energy conservation applies, meaning that

diffuse + specular = constant

In practice there's no human way to balance that equation, so some creative mind is useful. Inverting and applying a curve to the roughness map may give a satisfactory result for the specular map:

specularSource = curve (1 - roughnessPBR, 108 > 0, 208 > 112)) in sRGB

where the curve looks like this:

Curve to turn inverted roughness map to specular map

An interesting result of the function is that roughness higher than 64% sRGB will render zero specular reflectivity. Most of such textures are for coarse earth, fabric, concrete, etc. In Source convention, these materials generally don't have a specular map indeed.

Instead, it may be favorable to increase diffuse reflectivity for such rough surfaces. One possible solution is:

albedoSource = dodge ((25%) curve (roughnessPBR, 127 > 0, 191 > 16, 255 > 64), albedoSource) in sRGB

where the curve looks like this:

Curve to turn the roughness map into a mask on albedo


Metallic materials in this context are rare because most metals seen in the real world are oxidized or painted, which fall into the non-metal category. They don't have a bare metallic surface. Yet certain ceramics can have an extent of metalness as in some texture samples.

The following example shows a tile texture with homogeneous roughness overall but varying specular reflectivity. The lighter area in specular map represents increased metalness.

Ceramic texture example showing varying metalness

That increased metalness supposedly brings higher specular reflectivity. Hence, an additional function may be applied besides the steps above.

specularSource = screen (specularPBR, specularSource)

As for the rare, bare metal, its diffuse reflectivity is almost zero in reality. The perceived color is almost solely contributed by specular reflection. In Source, however, only diffuse reflection can be received by other surfaces in both diffuse and specular forms. Specular reflection can only be received by other surfaces in the same form, and even that requires running buildcubemaps multiple times. If you don't feel like messing with buildcubemaps and auxilliary lighting everywhere around the level, it's still recommended to take their perceptual color as albedo, just as non-metals. The distinction between metals and non-metals should be minimal, if any.

So if the texture has an albedo almost black, the idea is to light it with the specular map. Other processes should be similar to non-metals.


By this point, you should have a clue to prepare the albedo, the specular map and the normal map for Source. After that, you might want to create your material in a familiar way.

Note.png Note: The specular map is typically embedded into the alpha channel of the normal map, so you don't need to export it to your filesystem.

The formula constructed in this tutorial is aimed to be neutrally stylized, so it's no surprise if it fail for some extreme cases. You're encouraged to try it by yourself. Actually, it's more common to fit the textures for your own level instead of trying to reproduce the original appearance.

See Also