Team Fortress 2

Team Fortress 2

Not enough ratings
Static Prop Lighting
By worMatty
A short demonstration of static prop lighting options when making a map
   
Award
Favorite
Favorited
Unfavorite
Introduction
Static props are models in a map which do not move or change. They are part of the map's static geometry. Props which change somehow while the game is running (e.g. capture points, payload carts and shutter doors) are dynamic,

By default, props have origin lighting. Here, the entire prop is lit the same way. The colour and brightness of the shading is sampled from the prop's origin, which is a point in the model used for positioning. In the Hammer 2D grid, the origin is shown as a small 'x'.

However, it is possible for static props to have realistic gradation in shade and colour by changing your compile settings.
Enhancing Shading on Props
In the following examples, I will demonstrate different lighting compile settings on a small number of static props.

Origin Lighting
Origin lighting is what you get by default. The prop is shaded and coloured uniformly. This results in unrealistic lighting when a prop is half in shade.

Origin lighting can occasionally be useful for certain items important for gameplay, such as ammo boxes, to ensure they are easy to see. You can force a prop to use it in its settings, and even specify an info_lighting to sample for light and colour.

Per-Vertex Lighting
As the name suggests, per-vertex lighting gives each of the model's vertices (points where lines meet) its own colour value. This allows each part of the model to be lit differently.

Per-vertex lighting is generally the best option, but if a model has a simple wireframe, or the lighting has a high contrast between bright and dark (as it is in the screenshot), you can get unrealistic transitions. The detail of per-vertex lighting is dependent on two things: 1. The density of the mesh, 2. The lighting contrast.

Take a closer look at the bridge.
See how the shading breaks up on the left? Now look at the model in wireframe view using the console variable mat_wireframe 3:
The pattern of shading follows the model's triangles.

Static prop lighting can be added to the compile by supplying VRAD with the argument -StaticPropLighting.

Lightmaps on Static Props
Lightmaps are used by brushes and as the name suggests they are literally a map of light. They are a texture consisting of luxels (light pixels) which is created when the lighting is compiled.

Static props can use lightmaps but they generally do not work very well and should only be used in specific circumstances.
Notice how the simple, flat bridge has very accurate shading? Notice how the ladders do not.

Lightmaps on static props are overlaid with the model's textures. If the textures are repeated on the model for the sake of efficiency in texture file size, then so is the lightmap, which can result in incorrect shading. Shading on one part of the model could be re-used on another, in a place where it does not make sense.

Lightmaps could be useful when a prop is wide and flat, and has no repeating textures, and it does not have a dense mesh of vertices. In these cases they could be preferable to per-vertex lighting, but if the lighting is fairly consistent then there may be no visible benefit.

Lightmaps can be enabled in a prop's settings. The default lightmap resolution in the TF2 FGD is 32. In the example screenshots I used 16.
Enhancing the Definition of Shadows Cast by Static Props
Static Prop Polys

By default, the collision mesh of a model is used to cast shadows on the world, and this can often by quite coarse and bulky in comparison to the model's own mesh, resulting in blocky, ugly, unrealistic shadows when a model has lots of small details or hollow parts.


Adding the Static Prop Polys argument to VRAD causes it to use the model's mesh instead of its collision mesh.


Static props are also allowed to cast shadows onto themselves. This is known as self-shadowing, and is also enhanced when static prop polys is enabled. Most of the time this is desirable, but sometimes it's necessary to turn it off because it causes unrealistic shadows. In my experience this tends to be the case with trees.

See the collision model of the beam prop. You can do this yourself in-game using the console variable vcollide_wireframe 1:


The VRAD argument for this feature is -StaticPropPolys.
Texture Shadows
Have you ever had a situation where you have a prop with a clearly transparent texture, such as a chain-link fence, but frustratingly it casts a completely opaque shadow?

This is because VRAD doesn't use the transparency of textures in its shadow calculations. It uses the collision mesh, or the model's mesh if Static Prop Polys is enabled. Just look at how odd the shadows cast by these trees are:
Trees without texture shadows
It's possible to instruct VRAD to use the transparency of the model's textures to cast realistic and fine-detailed shadows:
Trees with texture shadows
To use this feature, add the -TextureShadows argument to VRAD. If the model was compiled with the $casttextureshadows parameter in its QC file, it will cast texture shadows. If it wasn't, then you will need to add the model to a list to force VRAD to compute them.

Create a new file in a text editor and add each model's filepath, prefixed by forcetextureshadow:
forcetextureshadow props_japan/sakura_tree_tall01.mdl forcetextureshadow props_japan/sakura_tree_tall02.mdl forcetextureshadow props_japan/sakura_tree01b.mdl forcetextureshadow props_japan/sakura_tree01c.mdl

Save the file in the same directory as your VMF as mapname.rad and VRAD will automatically find it during compile.

If you prefer, you can create a general .rad file listing all your models with translucent textures. Store it in your tf dir and add it to your VRAD command line using -lights filename.rad.

Improving shadow definition
If you desire to increase the definition or resolution of shadows, simply modify the 'lightmap luxel scale' of the brush faces which receive the shadows, using the texture application tool in Hammer (called the Face Edit Sheet, opened using the keyboard shortcut Shift + A).

The default is 16, and lower numbers such as 8 and 4 provide better definition. Always enter a value that's a power of two. Larger numbers can be used to soften shadows.

Be warned that increasing lightmap resolution will increase map file size, and there is a limit to the amount of lightmap data that can be stored in memory before the map cannot be run. Therefore you should adjust luxel scales on a case-by-case basis, decreasing the scale when you need extra detail, and increasing it when you can go without it, to save space. You can also cut up a brush face and have different lightmap scales on it.
Trees with texture shadows and a lightmap scale of 4
Ignore Surface Normals
Sometimes a model will have faces which are basically just one polygon but you can see the texture from both sides (i.e. the material does not use 'backface culling'). This is a neat trick but can result in peculiar shading because only the 'top' side of the face is shaded. For a tree, this can result in unnatural dark or inaccurate shading on the underside of leaves.

To fix this, in the prop's properties, set the 'Ignore surface normals for computing vertex lighting' setting to Yes, and both sides of the face will be used to calculate an average shading value.
Summary
  • -StaticPropLighting enables per-vertex lighting on static props.
  • -StaticPropPolys causes VRAD to use the model's mesh polygons for casting shadows, rather than its simpler collision model.
  • -TextureShadows causes models with transparent textures to cast more realistic shadows based on the textures, rather than just the mesh polygons. It's most often used on leaves and chain link fences but keep an eye out for other props which have transparent textures on flat faces. It will not work if the models are not specified in a RAD file.

Lightmaps on static props should only be used when there is a clear benefit, as they increase the file size.

Notes

If you use a custom version of VRAD, such as Slarti's Tools for SDK 2013, all three compile arguments may already be enabled by default, and adding them again will cause the compile to fail.

Here is more information on RAD files: https://developer.valvesoftware.com/wiki/VRAD#Lights_files
4 Comments
worMatty  [author] Mar 5, 2024 @ 5:06pm 
Good advice. I wasn't aware of that parameter. Thanks for sharing.
SirYodaJedi Mar 4, 2024 @ 7:07pm 
Adding models to the RAD file is not necessary if the model was compiled with $casttextureshadows in the QC file.
Rayiumi Dec 22, 2023 @ 9:24am 
ty
worMatty  [author] Jun 25, 2023 @ 7:55am 
Updated the guide today to make the language simpler and more concise.