Those who know me well know that for the past couple of years or so, I've been obsessed with light and shadow.
You can't be obsessed with something and not gain at least a little knowledge about it, so I'm going to share that knowledge with future generations, who will definitely be attentive to it and profit by it.
My original headers for this guide were written in order from most opinionated but most relevant, to least opinionated but least relevant, so naturally I'll write them in reverse order for the real thing.
Incidentally, this is sort of written for intermediate-advanced mappers. You're not likely to get much out of this if you're just starting out.
Without -final, all light rays will only be allowed to bounce once, so shadowy areas will be much darker and less detailed than they otherwise would be.
Without -staticproppolys, your props will use their collision mesh instead of their actual mesh to cast shadows.
This can range from mildly inaccurate, to very very inaccurate, to casting no shadows at all.
Without -staticproplighting, your props will use the light calculated at the infinitesimally small point of their origin to light their entire model.
This means that if the origin happens to be inside a wall or floor, the entire model will be dark.
With this parameter, each vertex of your prop will calculate its own light value, and then the engine will blend between each vertex to create a gradient on every surface, generally making the prop look much better.
As it happens, you can still observe the "props where their origin is inside a wall" thing even with -staticproplighting enabled. (The pipes from props_mining and props_hydro are a common example). What's going on!?
Any prop that uses $phong, including the aforementioned pipes, will be unable to utilise vertex lighting.
Also, if you use info_lighting for a prop, that prop will by definition be entirely lit by a single point.
So, if you've got a prop that's half in light and half in shadow, using an info_lighting can only make it appear to be entirely in light or entirely in shadow.
(This isn't entirely true - even non-vertex lighting still bases itself on the incoming light vectors and the normal vectors of the props' mesh, so triangles on the prop that are facing away from the light direction will be dark).
This deficiency of info_lighting spawned this rant from the legendary YM:
Also, before I forget - all prop_dynamic and prop_physics entities will not be able to use vertex lighting, so lighting on them can end up looking pretty crap.
Finally, without -textureshadows, any surface of a model that has a semitransparent texture will cast shadows as if entirely transparent.
With it, surfaces like grates can cast a grate-like shadow.
But there are MANY caveats.
Firstly - this only applies to models, so even if you compile with this parameter, your brushes with semitransparent textures will still seem fully transparent.
Secondly - only the models with a "forcetextureshadow" string defined in a "lights.rad" file can cast textureshadows with this compile parameter.
So if you have a custom prop that you want to cast textureshadows, you need to create your own version of this file and add it to your VRAD compile via the lights parameter.
I've attached my own custom lights.rad file with a bunch of extra forcetextureshadows defined to this post, so you can just use it instead of making your own one.
(You will need to change the file extension from .txt to .rad, since the site doesn't let me upload .rad files).
Here's what your VRAD settings should look like to include a custom file:
The file itself should be placed in "steamapps/common/Team Fortress 2/tf/", not in any subfolders.
With the combination of textureshadows and high-resolution lightmaps, you can achieve really striking shadows!
Despite all the caveats I spoke about for the last two, I consider the above four compile parameters essential to all VRAD compiles.
That's compile parameters done, now we can get on to the cool stuff!
So now let's talk about all the ways in which it's a horrible way to light props.
This is a door.
This is a prop that was introduced into the game with the inclusion of koth_king in 2011.
But it has a massive problem: It's a suitably low-poly prop for what it needs to be!
More specifically, the wireframe of its front face looks like this:
Lighting is decided on a per-vertex basis.
So if we overlay what the shadow SHOULD look like onto this wireframe, we can see what vertices are considered to be in shadow:
If we then compile the map, we can see that the door will look like this:
This is because all three vertices of the top right triangle are in shadow, so the engine assumes that the entire triangle must be in shadow.
This is the purest, and most common, example of the failures of vertex lighting.
You can fix this, for this door prop in particular, via three simple methods:
To understand what lightmapped props will do for us, let's replace the door prop with an exaggerated, brush-based equivalent (because brushes use lightmaps):
You can see that the shadow looks exactly as it should.
This is because lightmaps, being an image, represent points of light and shadow as evenly spaced pixels, instead of going by vertices which might be incredibly far apart and produce a low-resolution surface.
Here you can see what the lightmap pixels, or "luxels" on this door look like:
You can also see that the Lightmap Grid view previews props as solid white by default, because they don't have lightmaps.
To enable lightmapping on a prop, all you need to do is alter these parameters:
Then, if you're using Hammer++, you can preview the prop's lightmaps in the same Lightmap Grid View I was using earlier - however, the actual Lighting Preview will not preview that prop's lightmaps.
The interesting thing about prop lightmaps is that it overlays the lightmap luxels over the actual texture of the prop.
So, instead of setting how wide (in world units) each luxel is like you do for world brushes, you're setting how many luxels the image spans across.
What this means practically is that for a larger prop, you're going to need a higher resolution.
64x64 was pretty high-resolution for that door, but here's what 64x64 looks like on one of the conveyor belts from 2fort:
As you can see, it's pretty inadequate. Just think - if each of those pixels was a colour rather than a light value, you'd be forgiven for thinking that was a model from 1998.
Out of curiosity, let's check out how that door looks in-game:
Now, that's pretty cool! The door handle is casting a long shadow along the door, and every subtle protrusion from the door's surface has subtle but powerful ambient-occlusion-style shadows.
The vertices of this door prop are actually even lower-resolution than the metal one from earlier, so you could never get this with vertex lighting.
If this isn't a big enough upgrade in visual quality to convince you, then bow before the power of the radar dish example:
Now, that said, there are some caveats with lightmapped props.
Firstly, remember when I said earlier that phong-shaded props don't support vertex lighting? They also don't support lightmapping.
Additionally - and bear with me here - since lightmapped props use their texture to construct the lightmap, any model that reuses parts of the texture on multiple parts of the model will also reuse that same part of the lightmap.
Here's an example:
The arch is a lightmapped prop. We can see that from this angle, the inside is in shadow, because it's facing away from the sun.
By that logic, we should expect to have the inside be illuminated on the other side, so let's see if that's true:
Uh oh! It's not illuminated! And we can see that that's not just because the sun angle is too sharp, because the floor below the wall is clearly illuminated.
What's going on?
To put it simply, when some Valve modeller created this prop, they presumably only created the mesh and UV for one side, and then simply mirrored it to create the same side.
This means that both sides reuse the same UV, so if you enable lightmapping for this prop, both sides will have the same lighting, even if they really shouldn't.
You can also see this by looking above the arch, where the textures are clearly mirrored:
So, unfortunately, enabling lightmaps for this prop is almost never a good idea.
But it gets worse!
Propper is a program that turns VMF files into models that can be used in maps.
As it turns out, Propper doesn't create new and unique UVs for the props - it just references whatever base-game textures you used on the original brushes.
This means that it's very, very likely for props made by Propper to reuse parts of their UV and therefore create buggy lighting when lightmapped.
You can find out how to fix this here.
Here's a general set of guidelines for props that are likely to be broken in this way:
For instance, if a prop is entirely in darkness, it doesn't really matter how complicated the shadows on its SURFACE are, so you may as well use vertex lighting.
Similarly, if there are lights illuminating the prop, but the prop isn't likely to cast any complex shadows on itself, and there aren't any complex shadows being cast on its surface by other stuff, vertex lighting will do a completely fine job there too.
Finally, if a prop has a very vertex-dense surface, vertex lighting will be almost indistinguishable from lightmapping anyway, so why would you bother enabling lightmapping there?
One case where I would never, ever recommend enabling lightmapping is the rock props from props_mining, because they fit all criteria:
I've already mentioned that a lightmap is a texture on the surface of an object, where the pixels control what colour of light that object is receiving.
But there are various upsides, downsides and subtleties associated with this, which I'll go into now.
You may know already that you can control how dense the luxels on a brush surface are in the Face Edit Sheet:
The default value is 16, and since it's "Hammer units per luxel", it confusingly works the opposite way to prop lightmaps - a LOWER number means higher-resolution shadows on that surface.
You may also know that, since lightmaps are a texture that's saved in the map file, having high-quality lightmaps will increase your map's filesize and make it take longer to download.
So, in general, you should only use a size lower than 16 on surfaces which actually have the edge of a shadow, and if this surface happens to be massive, you should cut it up so that only the part of the surface which actually has the edge of the shadow has low luxel size.
This is important not only to save on filesize, but also because of the esoteric topic of lightmap paging.
Lightmap paging refers to the fact that Source has a limit on how large the lightmap on a surface can be.
Gigantic surfaces will, naturally, have a very large lightmap texture and go over this limit, so Source splits large surfaces up automatically so they fit inside this limit.
So, if you're lowering the luxel size on a surface, you're making it so that that surface will get split up more often.
This means that if you give a large surface a luxel size of 4, and then place an info_overlay on that surface, you'll get an error saying something like "info_overlay on too many faces - max 64" (and this error stops your compile!). This means that the SINGULAR surface the overlay is on had such a large lightmap texture that Source had to split it up into more than 64 faces.
The simple solution to this error is to split the face up manually.
You may also know that if you get too eager with low luxel size, your map will crash when you load it with an error message "EngineError: Engine hunk overflow!"
But what I discovered is that, fascinatingly, this isn't closely related to luxel size at all!
Firstly, it happens on maps where the creator has left all surfaces at the default luxel size on 16, but it only happens on VERY large maps - maps with a lot of faces.
What this indicates is that engine hunk overflow is actually a lot more closely related to the number of FACES in your map than the number of LUXELS, and the only reason low luxel sizes make you reach this error faster is because lightmap paging splits your map up so it has a massive number of faces.
Another facet of lightmap paging is how it interacts with displacements.
The engine can split brushes up to satisfy its lightmap paging requirements, but it can't do the same thing for displacements, because that would misalign the vertices (the same reason why you can't sew two displacements unless their original brushes have connecting edges).
So, it just imposes a hard limit on how small the luxel size on a given displacement can be, based on that displacement's size.
If you select a displacement bigger than roughly 512x512 and try setting its luxel size to 4, you'll find that Hammer CHANGES the displacement's luxel size to 5, which is disgusting and perverse and offends God.
Interestingly, this also affects people who don't give a fuck about changing luxel sizes in their map - because if you make a big enough displacement, even the default luxel size of 16 is too small to satisfy lightmap paging requirements!
You can generally see this in amateurishly-constructed 3D skyboxes.
While on the topic of 3D skyboxes - since 3D skyboxes take whatever the sky_camera sees and upscale it by 16 times, that means the lightmaps of any brushes in the 3D skybox look as though they're 16 times bigger than they should be!
Here's the point where the main map and 3D skybox connect in a map I once analysed, seen with mat_luxels 1:
If you've ever looked at a map's 3D skybox and thought that the lighting in it looks flat and dull, this is why.
You can easily solve this by using a luxel size of 1 instead of 16 on 3D skybox brushes - and, in the case of displacements, splitting them up a LOT so you can use a luxel size of 4 or 2.
Interestingly, this isn't a problem for 3D skybox props, since they generally use vertex lighting, and so their resolution doesn't change at all with scale.
Everyone knows what colour, brightness, inner angle and outer angle do, but what about the other ones?
Well, let's find out:
It only exists on light_spot, and all it does is control how blurry the edge of the light is:
A higher Focus value means blurrier edges, and a lower Focus value means sharper edges.
In my experience, a low Focus value can be good for a kind of "cinematic-spotlight" effect, where you have one light that seems really strong, and that helps to dominate the scene and draw your eye to it.
Attenuation describes how much the intensity of the light diminishes as the distance from the light entity increases.
By default, light attenuation is set to Quadratic.
This is good, because it matches the real-world light intensity formula i = i0 / d^2.
But, just like with the Focus value, sometimes an unrealistic light can be used for greater visual effect.
In particular, changing the attenuation from Quadratic to Linear or even Constant can be great to create a light that casts a longer, more powerful shadow than its Quadratic equivalent.
Here's a comparison:
You can also "mix" the values, for instance by setting the attenuation to be simultaneously Linear and Quadratic.
But I've played around with this a bit, and I found that the only combination that really gets any results is a Constant value of 10000 or thereabouts, a Linear value of 0 and a Quadratic value of 1.
Valve used this combination extensively in Half-Life 2, specifically in Ravenholm and Nova Prospekt - so, in other words, to do exactly what I said before, where a bright, sharp-edged spotlight in a dark scene dominates the scene and draws the viewer's eye.
And as it happens, this combination happens to look nearly identical to just plain old Linear attenuation.
So, I recommend using Linear attenuation whenever you want a bold shadow, because it's just the right combination of having almost no falloff, but not having none at all like Constant.
Also, point lights function very differently from spotlights as regards attenuation:
So, linear point lights generally look TERRIBLE, but if you're getting an unconvincing effect from your Quadratic point lights like in the left of this image, you may find it helpful to switch to Linear.
By the way, light_environment uses Constant attenuation. This cannot be altered.
One thing you may have noticed about light entities is that you can also set a "50% Distance" and "0% Distance".
These are the distances away from the light at which the light intensity will be 50% of the original, and 0% respectively.
If you set these distances, it will override your Constant/Linear/Quadratic attenuation settings.
Here are a few example settings:
You can also set "Hard Falloff", which theoretically makes lights fall off to exactly 0 brightness beyond the 0% distance.
In Hammer++, Lighting Preview makes this look like it actually works, but I'm fairly sure it doesn't work in-game. Or maybe the FGD I use is just gaslighting me?
Anyway, what I've found with manual falloff distances is that you're generally using them either to create a soft gradient falloff (which is what you'd expect from Quadratic attenuation), or a light that maintains its brightness at a long distance (which is what you'd expect from Linear attenuation).
So, I generally just use either Quadratic or Linear attenuation straight-up instead of bothering with manual distances.
SunSpreadAngle controls how diffuse shadows from the sun are, or in other words, how blurry the edge of the shadow is - similar to Focus in a light_spot.
But SunSpreadAngle does this in a really idiotic way - it basically just makes two copies of the shadow, and offsets them by +- the SunSpreadAngle.
So, at very high SunSpreadAngles, and especially when low luxel sizes get involved, shadows from the sun become horribly unconvincing.
As YM mentions in the guide, the default SunSpreadAngle is 5 degrees, but in real life the sun takes up 32 arcminutes in the sky angularly, or about 0.5 degrees.
So really, the default SunSpreadAngle should be 0.5.
Combining a low SunSpreadAngle and a high lightmap resolution can lead to beautiful, stark shadows from the sun.
I don't recommend setting the SunSpreadAngle any lower than 0.25 (even though some maps have it at 0!) because the sun shadows still need to have somewhat blurry edges.
I also don't recommend setting it to anything abve 1.25, even on cloudy days where the shadows should naturally be more diffuse.
But why is it my favourite?
The contrast between the intensely orange-yellow fluorescent tube and the blueish spotlight, lightbulb and shadowy areas creates a subtle, yet pleasing, blue-yellow contrast.
Another pleasing factor is the light-dark contrast I talked about earlier.
Basically, the point I'm trying to get across is that we want to avoid uniformly lit scenes.
Contrast is naturally pleasing to the eye, and can be created either through light vs. dark, or colours which are close to opposite on the colour wheel.
But generally, the most pleasing scenes will actually do both of these things.
And THAT'S why this room is still my favourite part out of all of the maps I've artpassed.
I also want to show you an example of a poorly lit room from koth_sawmill:
Not only is the entire room uniformly lit in white with nearly no dark parts, but Valve was actually so lazy that instead of using a light_spot for the light, they placed a point light directly under it and called it a day.
This means that the light coming out of the spotlight cone somehow... magically bends? and goes directly up into the ceiling, which is quite impossible.
Sawmill in general is an example of a map I find very poor-looking. The sunlight and shadows are almost the same colour (and yes, I know this happens in real life on cloudy days - why do you think people find cloudy days depressing-looking?), and nearly every room is lit by an absolute spam of spotlights and point lights which are all the same colour.
Don't be lazy like this in your maps.
At the very least, aim to light every room with at most two light sources, preferably spotlights or the sun so some parts of the room are dark, and try out interesting angles for your spotlights instead of pointing them straight down.
Having your scenes be uniformly lit also takes away a prime opportunity - that is, placing props specifically so that they cast an interesting shadow.
The example I always like to use is this crane:
By placing this complex object specifically on a wall that's receiving sunlight and has high lightmap resolution, I'm essentially getting two details for the price of one - the crane AND its shadow.
This is actually particularly beneficial, because shadows in Source are pre-saved into the map file - so the performance cost of having detailed shadows is... nothing!? And on top of that, the shadows don't fade out at a distance like most other engines - it's a veritable lifehack!
So, you can achieve much more detail in your map than you otherwise would've been able to, simply by specifically placing your details so they cast interesting shadows.
This is also another great case for vertex lighting - even if a prop isn't lightmapped, it can still be complex in shape, and therefore cast complex shadows onto a brush surface which IS lightmapped.
Coming up is the part I was saying was "most opinionated" at the start of the guide.
You may think that "All this time I've been saying stuff about real life, but TF2 is the funny painting/cartoon style game! It's not meant to resemble real life!"
While that is true to an extent, you'll find that most of the paintings TF2 was inspired by, and most of the painting-like scenes in TF2, actually follow the rules of promoting light-dark contrast and colour-contrast.
Additionally, since TF2 is using ray-traced lighting (exactly the way real light works) with the real-life light attenuation formula, it's actually using very realistic lighting. You also see this kind of thing in animated movies - even though they aren't trying to promote a realistic style, they're still using ray-tracing, and still generally using sunlight and sky colours that are possible in real life - because anything else makes the viewer subconsciously think your scenes look "wrong".
One thing I always hate to see is maps with pink skies and pink sunlight.
Pink skies are perfectly possible in real life - but the sunlight itself is almost NEVER pink.
In fact, the sunlight in real life is never really anything except yellow, white or yellow-white.
Additionally, if you're using a pink sky AND pink sunlight, you're almost BEGGING for a boring scene with no visual contrast.
It doesn't help that the maps that do that almost always insist on using really bright and often purple or pink ambient light, so there's hardly any light-dark contrast, or colour-contrast between sunlight and shadow.
I've run through all my notes now.
I never anticipated this guide to become a 4500 word behemoth, but that's what dumping the knowledge gained from a 2-year obsession will do, I guess.
And that knowledge - is all about light.
You can't be obsessed with something and not gain at least a little knowledge about it, so I'm going to share that knowledge with future generations, who will definitely be attentive to it and profit by it.
My original headers for this guide were written in order from most opinionated but most relevant, to least opinionated but least relevant, so naturally I'll write them in reverse order for the real thing.
Incidentally, this is sort of written for intermediate-advanced mappers. You're not likely to get much out of this if you're just starting out.
Prop Lighting
VRAD Compile Parameters
This is essentially just paraphrasing this guide.Without -final, all light rays will only be allowed to bounce once, so shadowy areas will be much darker and less detailed than they otherwise would be.
Without -staticproppolys, your props will use their collision mesh instead of their actual mesh to cast shadows.
This can range from mildly inaccurate, to very very inaccurate, to casting no shadows at all.
Without -staticproplighting, your props will use the light calculated at the infinitesimally small point of their origin to light their entire model.
This means that if the origin happens to be inside a wall or floor, the entire model will be dark.
With this parameter, each vertex of your prop will calculate its own light value, and then the engine will blend between each vertex to create a gradient on every surface, generally making the prop look much better.
As it happens, you can still observe the "props where their origin is inside a wall" thing even with -staticproplighting enabled. (The pipes from props_mining and props_hydro are a common example). What's going on!?
Any prop that uses $phong, including the aforementioned pipes, will be unable to utilise vertex lighting.
Also, if you use info_lighting for a prop, that prop will by definition be entirely lit by a single point.
So, if you've got a prop that's half in light and half in shadow, using an info_lighting can only make it appear to be entirely in light or entirely in shadow.
(This isn't entirely true - even non-vertex lighting still bases itself on the incoming light vectors and the normal vectors of the props' mesh, so triangles on the prop that are facing away from the light direction will be dark).
This deficiency of info_lighting spawned this rant from the legendary YM:
Also, before I forget - all prop_dynamic and prop_physics entities will not be able to use vertex lighting, so lighting on them can end up looking pretty crap.
Finally, without -textureshadows, any surface of a model that has a semitransparent texture will cast shadows as if entirely transparent.
With it, surfaces like grates can cast a grate-like shadow.
But there are MANY caveats.
Firstly - this only applies to models, so even if you compile with this parameter, your brushes with semitransparent textures will still seem fully transparent.
Secondly - only the models with a "forcetextureshadow" string defined in a "lights.rad" file can cast textureshadows with this compile parameter.
So if you have a custom prop that you want to cast textureshadows, you need to create your own version of this file and add it to your VRAD compile via the lights parameter.
I've attached my own custom lights.rad file with a bunch of extra forcetextureshadows defined to this post, so you can just use it instead of making your own one.
(You will need to change the file extension from .txt to .rad, since the site doesn't let me upload .rad files).
Here's what your VRAD settings should look like to include a custom file:
The file itself should be placed in "steamapps/common/Team Fortress 2/tf/", not in any subfolders.
With the combination of textureshadows and high-resolution lightmaps, you can achieve really striking shadows!
Despite all the caveats I spoke about for the last two, I consider the above four compile parameters essential to all VRAD compiles.
That's compile parameters done, now we can get on to the cool stuff!
Lightmapped Props
If you're really good at being told what to think, then after the previous section you may be thinking "Vertex lighting is the best way to light props!"So now let's talk about all the ways in which it's a horrible way to light props.
This is a door.
This is a prop that was introduced into the game with the inclusion of koth_king in 2011.
But it has a massive problem: It's a suitably low-poly prop for what it needs to be!
More specifically, the wireframe of its front face looks like this:
Lighting is decided on a per-vertex basis.
So if we overlay what the shadow SHOULD look like onto this wireframe, we can see what vertices are considered to be in shadow:
If we then compile the map, we can see that the door will look like this:
This is because all three vertices of the top right triangle are in shadow, so the engine assumes that the entire triangle must be in shadow.
This is the purest, and most common, example of the failures of vertex lighting.
You can fix this, for this door prop in particular, via three simple methods:
- Set the door prop's "Disable Vertex Lighting?" property to Yes
- Use an info_lighting to disable vertex lighting
- Set the door prop's "Disable self-shadowing from vertex lighting" property to Yes
To understand what lightmapped props will do for us, let's replace the door prop with an exaggerated, brush-based equivalent (because brushes use lightmaps):
You can see that the shadow looks exactly as it should.
This is because lightmaps, being an image, represent points of light and shadow as evenly spaced pixels, instead of going by vertices which might be incredibly far apart and produce a low-resolution surface.
Here you can see what the lightmap pixels, or "luxels" on this door look like:
You can also see that the Lightmap Grid view previews props as solid white by default, because they don't have lightmaps.
To enable lightmapping on a prop, all you need to do is alter these parameters:
Then, if you're using Hammer++, you can preview the prop's lightmaps in the same Lightmap Grid View I was using earlier - however, the actual Lighting Preview will not preview that prop's lightmaps.
The interesting thing about prop lightmaps is that it overlays the lightmap luxels over the actual texture of the prop.
So, instead of setting how wide (in world units) each luxel is like you do for world brushes, you're setting how many luxels the image spans across.
What this means practically is that for a larger prop, you're going to need a higher resolution.
64x64 was pretty high-resolution for that door, but here's what 64x64 looks like on one of the conveyor belts from 2fort:
As you can see, it's pretty inadequate. Just think - if each of those pixels was a colour rather than a light value, you'd be forgiven for thinking that was a model from 1998.
Out of curiosity, let's check out how that door looks in-game:
Now, that's pretty cool! The door handle is casting a long shadow along the door, and every subtle protrusion from the door's surface has subtle but powerful ambient-occlusion-style shadows.
The vertices of this door prop are actually even lower-resolution than the metal one from earlier, so you could never get this with vertex lighting.
If this isn't a big enough upgrade in visual quality to convince you, then bow before the power of the radar dish example:
Vertex Lighting | Lightmaps (192x192) |
Now, that said, there are some caveats with lightmapped props.
Firstly, remember when I said earlier that phong-shaded props don't support vertex lighting? They also don't support lightmapping.
Additionally - and bear with me here - since lightmapped props use their texture to construct the lightmap, any model that reuses parts of the texture on multiple parts of the model will also reuse that same part of the lightmap.
Here's an example:
The arch is a lightmapped prop. We can see that from this angle, the inside is in shadow, because it's facing away from the sun.
By that logic, we should expect to have the inside be illuminated on the other side, so let's see if that's true:
Uh oh! It's not illuminated! And we can see that that's not just because the sun angle is too sharp, because the floor below the wall is clearly illuminated.
What's going on?
To put it simply, when some Valve modeller created this prop, they presumably only created the mesh and UV for one side, and then simply mirrored it to create the same side.
This means that both sides reuse the same UV, so if you enable lightmapping for this prop, both sides will have the same lighting, even if they really shouldn't.
You can also see this by looking above the arch, where the textures are clearly mirrored:
So, unfortunately, enabling lightmaps for this prop is almost never a good idea.
But it gets worse!
Propper is a program that turns VMF files into models that can be used in maps.
As it turns out, Propper doesn't create new and unique UVs for the props - it just references whatever base-game textures you used on the original brushes.
This means that it's very, very likely for props made by Propper to reuse parts of their UV and therefore create buggy lighting when lightmapped.
You can find out how to fix this here.
Here's a general set of guidelines for props that are likely to be broken in this way:
- Most foliage
- Most structures that're made out of a lot of planks
- Props that were clearly made originally by some internal tool Valve used to convert brushes to props
- (fairly sure) The metal door prop I used as an example, funnily enough
- Props which act as a cluster of a number of smaller props
- Any of the corrugated metal props from props_2fort (because as YM mentioned in the guide earlier, they're made up of a single set of polygons, which we can expand to understand that both sides are sharing the same UV)
- In the props_island folder, a lot of props have their textures in the same image file as other props - this is so that when you have a lot of props in a scene, you're making fewer unique draw calls for their UVs... or something. I'm not actually sure whether this breaks lightmapping, though.
- models/props_hydro/dumptruck.mdl
- models/props_mining/industrial_pipe01_128_01.mdl and all similar variants
- models/props_hydro/2pipe_straight_128.mdl and all similar variants
- models/props_hydro/3pipe_straight_128.mdl and all similar variants
- models/props_2fort/milkjug001.mdl
- models/props_hydro/water_machinery1.mdl and all similar variants
- models/props_gameplay/resupply_locker.mdl
- models/props_gameplay/cap_point_base.mdl
For instance, if a prop is entirely in darkness, it doesn't really matter how complicated the shadows on its SURFACE are, so you may as well use vertex lighting.
Similarly, if there are lights illuminating the prop, but the prop isn't likely to cast any complex shadows on itself, and there aren't any complex shadows being cast on its surface by other stuff, vertex lighting will do a completely fine job there too.
Finally, if a prop has a very vertex-dense surface, vertex lighting will be almost indistinguishable from lightmapping anyway, so why would you bother enabling lightmapping there?
One case where I would never, ever recommend enabling lightmapping is the rock props from props_mining, because they fit all criteria:
- They're not likely to cast interesting self shadows
- Their surface is decently vertex-dense, and more importantly the vertices are very UNIFORM, so unlike the door props from earlier, you don't get massive areas on their surface which are represented by only two triangles
Lightmaps
Now that you're good and bored of me talking about lightmaps, let's talk some more about lightmaps!I've already mentioned that a lightmap is a texture on the surface of an object, where the pixels control what colour of light that object is receiving.
But there are various upsides, downsides and subtleties associated with this, which I'll go into now.
You may know already that you can control how dense the luxels on a brush surface are in the Face Edit Sheet:
The default value is 16, and since it's "Hammer units per luxel", it confusingly works the opposite way to prop lightmaps - a LOWER number means higher-resolution shadows on that surface.
You may also know that, since lightmaps are a texture that's saved in the map file, having high-quality lightmaps will increase your map's filesize and make it take longer to download.
So, in general, you should only use a size lower than 16 on surfaces which actually have the edge of a shadow, and if this surface happens to be massive, you should cut it up so that only the part of the surface which actually has the edge of the shadow has low luxel size.
This is important not only to save on filesize, but also because of the esoteric topic of lightmap paging.
Lightmap paging refers to the fact that Source has a limit on how large the lightmap on a surface can be.
Gigantic surfaces will, naturally, have a very large lightmap texture and go over this limit, so Source splits large surfaces up automatically so they fit inside this limit.
So, if you're lowering the luxel size on a surface, you're making it so that that surface will get split up more often.
This means that if you give a large surface a luxel size of 4, and then place an info_overlay on that surface, you'll get an error saying something like "info_overlay on too many faces - max 64" (and this error stops your compile!). This means that the SINGULAR surface the overlay is on had such a large lightmap texture that Source had to split it up into more than 64 faces.
The simple solution to this error is to split the face up manually.
You may also know that if you get too eager with low luxel size, your map will crash when you load it with an error message "EngineError: Engine hunk overflow!"
But what I discovered is that, fascinatingly, this isn't closely related to luxel size at all!
Firstly, it happens on maps where the creator has left all surfaces at the default luxel size on 16, but it only happens on VERY large maps - maps with a lot of faces.
What this indicates is that engine hunk overflow is actually a lot more closely related to the number of FACES in your map than the number of LUXELS, and the only reason low luxel sizes make you reach this error faster is because lightmap paging splits your map up so it has a massive number of faces.
Another facet of lightmap paging is how it interacts with displacements.
The engine can split brushes up to satisfy its lightmap paging requirements, but it can't do the same thing for displacements, because that would misalign the vertices (the same reason why you can't sew two displacements unless their original brushes have connecting edges).
So, it just imposes a hard limit on how small the luxel size on a given displacement can be, based on that displacement's size.
If you select a displacement bigger than roughly 512x512 and try setting its luxel size to 4, you'll find that Hammer CHANGES the displacement's luxel size to 5, which is disgusting and perverse and offends God.
Interestingly, this also affects people who don't give a fuck about changing luxel sizes in their map - because if you make a big enough displacement, even the default luxel size of 16 is too small to satisfy lightmap paging requirements!
You can generally see this in amateurishly-constructed 3D skyboxes.
While on the topic of 3D skyboxes - since 3D skyboxes take whatever the sky_camera sees and upscale it by 16 times, that means the lightmaps of any brushes in the 3D skybox look as though they're 16 times bigger than they should be!
Here's the point where the main map and 3D skybox connect in a map I once analysed, seen with mat_luxels 1:
If you've ever looked at a map's 3D skybox and thought that the lighting in it looks flat and dull, this is why.
You can easily solve this by using a luxel size of 1 instead of 16 on 3D skybox brushes - and, in the case of displacements, splitting them up a LOT so you can use a luxel size of 4 or 2.
Interestingly, this isn't a problem for 3D skybox props, since they generally use vertex lighting, and so their resolution doesn't change at all with scale.
Light Parameters
There are a lot of parameters on light entities that barely anyone explores.Everyone knows what colour, brightness, inner angle and outer angle do, but what about the other ones?
Well, let's find out:
Focus
Focus is the least interesting of the unexplored light keyvalues.It only exists on light_spot, and all it does is control how blurry the edge of the light is:
A higher Focus value means blurrier edges, and a lower Focus value means sharper edges.
In my experience, a low Focus value can be good for a kind of "cinematic-spotlight" effect, where you have one light that seems really strong, and that helps to dominate the scene and draw your eye to it.
Attenuation
Attenuation describes how much the intensity of the light diminishes as the distance from the light entity increases.
By default, light attenuation is set to Quadratic.
This is good, because it matches the real-world light intensity formula i = i0 / d^2.
But, just like with the Focus value, sometimes an unrealistic light can be used for greater visual effect.
In particular, changing the attenuation from Quadratic to Linear or even Constant can be great to create a light that casts a longer, more powerful shadow than its Quadratic equivalent.
Here's a comparison:
You can also "mix" the values, for instance by setting the attenuation to be simultaneously Linear and Quadratic.
But I've played around with this a bit, and I found that the only combination that really gets any results is a Constant value of 10000 or thereabouts, a Linear value of 0 and a Quadratic value of 1.
Valve used this combination extensively in Half-Life 2, specifically in Ravenholm and Nova Prospekt - so, in other words, to do exactly what I said before, where a bright, sharp-edged spotlight in a dark scene dominates the scene and draws the viewer's eye.
And as it happens, this combination happens to look nearly identical to just plain old Linear attenuation.
So, I recommend using Linear attenuation whenever you want a bold shadow, because it's just the right combination of having almost no falloff, but not having none at all like Constant.
Also, point lights function very differently from spotlights as regards attenuation:
So, linear point lights generally look TERRIBLE, but if you're getting an unconvincing effect from your Quadratic point lights like in the left of this image, you may find it helpful to switch to Linear.
By the way, light_environment uses Constant attenuation. This cannot be altered.
One thing you may have noticed about light entities is that you can also set a "50% Distance" and "0% Distance".
These are the distances away from the light at which the light intensity will be 50% of the original, and 0% respectively.
If you set these distances, it will override your Constant/Linear/Quadratic attenuation settings.
Here are a few example settings:
You can also set "Hard Falloff", which theoretically makes lights fall off to exactly 0 brightness beyond the 0% distance.
In Hammer++, Lighting Preview makes this look like it actually works, but I'm fairly sure it doesn't work in-game. Or maybe the FGD I use is just gaslighting me?
Anyway, what I've found with manual falloff distances is that you're generally using them either to create a soft gradient falloff (which is what you'd expect from Quadratic attenuation), or a light that maintains its brightness at a long distance (which is what you'd expect from Linear attenuation).
So, I generally just use either Quadratic or Linear attenuation straight-up instead of bothering with manual distances.
SunSpreadAngle
This is basically going to just paraphrase this guide.SunSpreadAngle controls how diffuse shadows from the sun are, or in other words, how blurry the edge of the shadow is - similar to Focus in a light_spot.
But SunSpreadAngle does this in a really idiotic way - it basically just makes two copies of the shadow, and offsets them by +- the SunSpreadAngle.
So, at very high SunSpreadAngles, and especially when low luxel sizes get involved, shadows from the sun become horribly unconvincing.
As YM mentions in the guide, the default SunSpreadAngle is 5 degrees, but in real life the sun takes up 32 arcminutes in the sky angularly, or about 0.5 degrees.
So really, the default SunSpreadAngle should be 0.5.
Combining a low SunSpreadAngle and a high lightmap resolution can lead to beautiful, stark shadows from the sun.
I don't recommend setting the SunSpreadAngle any lower than 0.25 (even though some maps have it at 0!) because the sun shadows still need to have somewhat blurry edges.
I also don't recommend setting it to anything abve 1.25, even on cloudy days where the shadows should naturally be more diffuse.
Colour & Design Theory
Out of all the maps I've artpassed, in which I've spent hours carefully poring over the lighting in each and every one of them, this scene from cp_bruhstbowl is still my favourite:But why is it my favourite?
- Low luxel sizes are used, but not exclusively to cast complex shadows - instead, the simple shadows cast by the yellow light along the floors and walls are rendered in high detail, and the subtle shadow of the inset doorframe onto the door within is also rendered in high detail
- The lights are positioned in such a way that only one surface of each object is illuminated brightly - so, even on different faces of the same object you get a pleasing light-dark contrast
- The room has several light sources in it, but parts of it are still dark
The contrast between the intensely orange-yellow fluorescent tube and the blueish spotlight, lightbulb and shadowy areas creates a subtle, yet pleasing, blue-yellow contrast.
Another pleasing factor is the light-dark contrast I talked about earlier.
Basically, the point I'm trying to get across is that we want to avoid uniformly lit scenes.
Contrast is naturally pleasing to the eye, and can be created either through light vs. dark, or colours which are close to opposite on the colour wheel.
But generally, the most pleasing scenes will actually do both of these things.
And THAT'S why this room is still my favourite part out of all of the maps I've artpassed.
I also want to show you an example of a poorly lit room from koth_sawmill:
Not only is the entire room uniformly lit in white with nearly no dark parts, but Valve was actually so lazy that instead of using a light_spot for the light, they placed a point light directly under it and called it a day.
This means that the light coming out of the spotlight cone somehow... magically bends? and goes directly up into the ceiling, which is quite impossible.
Sawmill in general is an example of a map I find very poor-looking. The sunlight and shadows are almost the same colour (and yes, I know this happens in real life on cloudy days - why do you think people find cloudy days depressing-looking?), and nearly every room is lit by an absolute spam of spotlights and point lights which are all the same colour.
Don't be lazy like this in your maps.
At the very least, aim to light every room with at most two light sources, preferably spotlights or the sun so some parts of the room are dark, and try out interesting angles for your spotlights instead of pointing them straight down.
Having your scenes be uniformly lit also takes away a prime opportunity - that is, placing props specifically so that they cast an interesting shadow.
The example I always like to use is this crane:
By placing this complex object specifically on a wall that's receiving sunlight and has high lightmap resolution, I'm essentially getting two details for the price of one - the crane AND its shadow.
This is actually particularly beneficial, because shadows in Source are pre-saved into the map file - so the performance cost of having detailed shadows is... nothing!? And on top of that, the shadows don't fade out at a distance like most other engines - it's a veritable lifehack!
So, you can achieve much more detail in your map than you otherwise would've been able to, simply by specifically placing your details so they cast interesting shadows.
This is also another great case for vertex lighting - even if a prop isn't lightmapped, it can still be complex in shape, and therefore cast complex shadows onto a brush surface which IS lightmapped.
Coming up is the part I was saying was "most opinionated" at the start of the guide.
You may think that "All this time I've been saying stuff about real life, but TF2 is the funny painting/cartoon style game! It's not meant to resemble real life!"
While that is true to an extent, you'll find that most of the paintings TF2 was inspired by, and most of the painting-like scenes in TF2, actually follow the rules of promoting light-dark contrast and colour-contrast.
Additionally, since TF2 is using ray-traced lighting (exactly the way real light works) with the real-life light attenuation formula, it's actually using very realistic lighting. You also see this kind of thing in animated movies - even though they aren't trying to promote a realistic style, they're still using ray-tracing, and still generally using sunlight and sky colours that are possible in real life - because anything else makes the viewer subconsciously think your scenes look "wrong".
One thing I always hate to see is maps with pink skies and pink sunlight.
Pink skies are perfectly possible in real life - but the sunlight itself is almost NEVER pink.
In fact, the sunlight in real life is never really anything except yellow, white or yellow-white.
Additionally, if you're using a pink sky AND pink sunlight, you're almost BEGGING for a boring scene with no visual contrast.
It doesn't help that the maps that do that almost always insist on using really bright and often purple or pink ambient light, so there's hardly any light-dark contrast, or colour-contrast between sunlight and shadow.
I've run through all my notes now.
I never anticipated this guide to become a 4500 word behemoth, but that's what dumping the knowledge gained from a 2-year obsession will do, I guess.
And that knowledge - is all about light.
Attachments
Last edited: