Optimization Tutorial: Difference between revisions
mNo edit summary |
m (→Console commands: fixed grammar & spelling) |
||
Line 421: | Line 421: | ||
==Console commands== | ==Console commands== | ||
How can you see how much/what is rendered? Well, you can only see it | How can you see how much/what is rendered? Well, you can only see it in-game. | ||
So, go compile your level (don't use a fast vis | So, go compile your level (don't use a fast vis; it won't work optimally and it will give you a wrong perception as to what you have to change) and go to the console. First type "sv_cheats 1" (without the quotes). This is necessary for some of the commands to work because they can be used as a cheat, as if you wallhax0rs didn't know. ;-) | ||
Useful ones are: | Useful ones are: | ||
===mat_wireframe 1=== | ===mat_wireframe 1=== | ||
Shows everything the engine draws, including models, but only the outer lines (toggle off with "mat_wireframe 0") Therefore, you can see through walls to see | Shows everything the engine draws, including models, but only the outer lines (toggle off with "mat_wireframe 0") Therefore, you can see through walls to see EXACTLY everything the engine draws. If you were Weeble or Bob, you wouldn't see the pipes without mat_wireframe on. | ||
Mat_wireframe is also useful to check if and how your occluders and areaportals are working. | Mat_wireframe is also useful to check if and how your occluders and areaportals are working. |
Revision as of 15:59, 21 January 2008

For help, see the VDC Editing Help and Wikipedia cleanup process. Also, remember to check for any notes left by the tagger at this article's talk page.

There are a lot of ways you can optimize your map. I will try to deal with all of them, in one, orderly tutorial. There are two things you can optimize with this tutorial, the compile time (especially with vvis you can save hours) and offcourse the map itself while running it ingame
What I will discuss (be prepared: this is everything I could find about optimization!):
General Notices
First things first, the easiest way to optimize your map is careful building. A few tips:
- Donât enclose your entire level in one box (saves both compile times and map speed).
- This will generate extra leafs, lightmaps, clipping hulls, faces to be drawn, etc.
- This includes skyboxes! The ideal skybox is as small as possible:
|
Fix leaks (increases map speed)
With leaks, vbsp wont be able to divide your level into leaves (see chapter "Hints"), so it wont create a portal file. Therefore, you can't use vvis or Glview. Leaks also make vbsp not know which faces it can skip, making your map only laggier and bigger, and vrad will not be able to light your map as good as without leaks. Water will also not work. Leaks are holes in you map. Since the compiler will try to find out what the inside of your level is, so it doesn't have to compile the outside, it will not be able to do that if the inside of your level is in contact with the outside. Think of your map as a spaceship: only when its hull is completely air-tight, people (all the entities) will be able to live inside. If there's a hole leaking into space (the void), everybody will die. On our spaceship that is. Not in HL2...
How to find leaks?
First of all, vbsp will write an error in the compile log. The leak error doesn't stand out, but if you look careful you can see something like:
**** leaked ****
Entity info_player_deathmatch (22.94 -64.44 17.00) leaked!
It will report an entity (e.g. info_player_deathmatch) and some coordinates.
Now, when you go to [map->load pointfile] and load your pointfile (Usually Hammer can find it. If not, you'll have to search manually for it. (It's called "[mapname].lin" or "[mapname].pts".)) a red line will appear, starting at the stated entity, and ending outside your level (in the black space, called the "void"). If you just follow that line, it will eventually pass through your leaking hole. If you cant find the entity the line starts, go to [tools-> go to coordinates] and fill in the coordinates mentioned in the log. Remember entities and displacements can't block leaks. Therefore it's easiest to hide all entities and displacements so you can spot the leak easier. (You might have turned a wall into an entity which was supposed to seal your level for instance.) In space you can't seal holes in a spaceship with humans, in maps you can't seal holes with entities. Simple as that. Better use vis-groups to hide all the entities and displacements. Removing the mentioned entity will only help if the entity itself is outside the level, but the level itself is sealed (e.g. when a person from our sealed spaceship is going for a space-walk).
Also, leaks may be caused by invalid use of areaportals (in our spaceship, areaportal(window)s are airlocks inside bulkheads), but more on that here.
If you've fixed the leak, there may still be another one. Therefore, unload the pointfile (under [menu] )and recompile. If there's another leak, simply load the pointfile again. Keep doing that until there are no more leaks.
Don't make world-brushes overlap
Vvis doesn't like it, and it may create extra faces. It will also make your map less orderly. Your choice.
Don't overdo dynamic lights (increases map speed)
Simply don't because they are quite hard to render. HL2 simply isn't doom3. Also, if you are using point_spotlights, check the "no dynamic light"-flag, to see a big increase in performance.
Make brushes standard sizes
Not only will it be easier for you to use them, but its also easier to find and prevent (small) leaks.
- Standard sizes are 8, 16, 32, 64, 128, 256, 512, 1024 units.
Some values inbetween (48, 96 etc) can also be "valid" for some textures. Using standard sizes will also enable you to stay on the grid and use textures without the need to shrink or stretch them to awkward values. This is not a hard rule, but it will make your life so much easier. To help doing this, I suggest you use a big grid (16 units) and only decrease its size when your making detail brushes.
Also, don't forget to keep your brushes on the grid, not doing so will result in a less orderly, bigger map.
Don't use carve for any non-square brushes
And if you do, tidy it up after. If a wall consisting of dozens of brushes due to carving of a cylinder in it wont slow down you map, I don't know what will. The keypoints here are to:
- Minimize brushes and faces as much as possible
- Making sure all points are on the grid and prefeably as straight as possible so they are easier to handle, and less prone to make errors.
Don't overdo water (increases map speed)
On multiplayer-maps water usually is a no go, because it means a lot of lag. (unless some steam update fixed that) So think twice before you try to use it.
If your still set on using it, but find it creates (a little too) much lag, you can do the following things:
- Change the type of water to cheap water. Either use the water_lod_controls-entity or just stick a water-texture with "cheap" in its name on the water brush.
The water_lod_control entity changes the water from expensive to cheap as soon as the player is far enough from the water. - Reduce the size of your water brushes. Make water brushes as small as possible, NEVER let them overlap world-brushes, because they will still be rendered but never be seen. Also, when dealing with displacements, remember water continues under them but the player will probably never see it.
- Stick as few entities inside as possible. Any model touching the water (its bounding box to be exact) will be rendered with water-visibility-features, but they come with a higher price (they are essentially rendered TWICE. To reduce load, make sure model-entities that are outside the water don't touch the water with any part of their bounding box, if you don't want them to. Also remember when your water runs under displacements and you have some model sticking through the same displacement, it might still touch the water:
http://www.student.kun.nl/rvanhoorn/Optimization/water.JPG
Like in this picture, where the green line is a displacement and the blue is a water brush. The pink object sticks in the water, meaning it needs to be rendered heavier, even though you wouldn't be able to see it sticking inside the water. Also, this water brush is much bigger than it has to be; it could stop at the place where its top and the displacement (green) meet.
Use "mat_showwatertextures 1" in the console to find out which entities are rendered twice because they touch the water-surface.
Map wisely
If the entire map can be seen from one point, theres not a lot of optimization you can do. Try to avoid large, open area's since you can't optimize them. Instead, try to block the player's view (that of vvis to be exact) with buildings or walls to make sure you can actually hide parts where the player isn't looking. (with or without the use of entities and hints) Also, adding lost of corners instead of straight hallways may enable you to optimize your map better, but it comes at a cost: Will your map still be any fun? Will it still look great? (did it ever?)
If these general rules don't help you, youll need heavier stuff. Bring in the artillery!!
Nodraw texture
This magical texture makes all faces which it is put on disappear completely. You apply it as any other texture. You can find it searching for "nodraw" in the texture browser. When you use it on a face, the engine doesn't have to draw that face at all, meaning less load and faster maps. The effect for one face is minimal, but using it massively is a different matter. So when do you use it? As much as possible! Any face the player will never see, should get this nodraw texture. Think about things like the rear of walls the player never gets to see, or the roof of buildings, walls that are covered by entities (e.g. func_details). You can also use brushes with "nodraw" on all sides of a brush to close holes created by displacements (displacements don't seal leaks, but "nodraw"-brushes do). Only one exception: "nodraw" doesn't need to be applied to faces on the outside of the level, or ones that are already covered entirely by other world-brushes or func_details, since those faces are automatically removed by vbsp. Brushes with "nodraw" on all sides (I will call them NODRAW-brushes from now on) are still solid, even though you can't see them! If you look at a brush with nodraw, you will either see a skybox (if your map has one), other parts of your level (if their leaf is visible and are behind the nodraw) or just HOM-effects (halls of mirrors, the effect when a certain part of your view isn't updated, looks like a hall of mirrors...)
You may prefer to build your entire level with "nodraw"-textures and only texture the faces you can actually see, to make sure the engine doesn't render more faces than it has to.
Nodraw can't be applied to displacements, luckily there's rarely a reason for it.

func_detail
The func_detail is a strange entity, because it's only handled by the compiler. The func_detail is only used to give vvis an easier job figuring out your level (to find out why, see chapter "Hints").
How does it work?
Well, as soon as you tie a brush (or a bunch of them) to a func_detail, these brushes become entities. Entities can't seal leaks, but they are also ignored by vvis. This can save hours of compiling, simply because vvis doesn't have to calculate what you can and cannot see when you are looking through these brushes. Another great thing about this method is, that you can predict way more accurate how your level will be divided into leaves, so its easier to manipulate them. Func_details act like world-brushes for the rest of their lives, so they cast shadows and are solid, but they won't cut up geometry like world-brushes. They won't block visibility (the engine will render everything behind them), but that's the only, and most negative point about these entities. So don't make walls func_detail if they can block visibility to big/complicated rooms.
Where to use func_details?
Again, as much as possible. Good candidates are brushes that never have to block visibility, like pillars, stairs, fences or roofs of houses that jut out, complicated brushes like brushbased machines, anything that's round, or plain walls you don't want to cut up leafs, like the walls above doors, below and above windows (exceptions are when areaportals are used). I'm sure you can think of other uses for this great non-entity. Since func_details can't seal leaks, don't use them on outside walls of your level, and because they are ignored by vvis, don't tie entire walls to func_details because vvis will think you can see everything behind it (and the game will try to render that too).
In the following example, there's a small level with a house and two cylinders. In one shot, the cylinders and the roof of the house are made with func_details, on the other, they aren't. The effects are amazing to say the least. Remember, each line represents a border of a visleaf, and each leaf add to the time vis takes to figure out your map!
- The map (notice the two cylinders and the roof of the house).
http://www.student.kun.nl/rvanhoorn/Optimization/detail1.JPG - No func_detail, seen in glview (tool to display all leafs in your level, see chapter "Hints" to find out how to use it.) I can count atleast dozens of leafs here
http://www.student.kun.nl/rvanhoorn/Optimization/detail2.JPG - func_detail (I know it's still not optimal, I could make the slanted roof a func_detail and save even more leafs. Remember however, that this may cause you to see more leafs and actually make your map laggier.) Only six leafs here...Now that's style!
http://www.student.kun.nl/rvanhoorn/Optimization/detail3.JPG - One last example is the stairs. there are two ways to make it into a func_detail:
http://www.student.kun.nl/rvanhoorn/Optimization/detail4.JPG
- (left) You can not tie anything to func_detail and suffer from extra vistime (each step will create another leaf).
- (middle) You can tie the entire stair, including whats under it to func_detail, but thats not always possible, so there's a third way.
- (right) Lastly, you can choose to make the steps themselves func_detail while leaving the rest of the stairs normal. Only one leaf is created this way (with a slanted underside) but this is very acceptable. Its all up to your design. Remember to give the unseen faces a "NODRAW"-texture for extra safety.
The example map from Valve about func_detail can be found here: "sourcesdk_content\hl2\mapsrc\sdk_func_detail.vmf"
Hints (and introduction to visleafs)
To understand hints and be able to use them well, you'll have to understand the entire vvis-process. When you run vbsp, it'll cut your level into area's, called leafs. It'll store its information in a portalfile (*.prt) which vvis pickes up to use.
What vvis does next is calculate which leafs can see which and when you are inside your map in the game. The engine only has to render the leaves that can be seen by the leaf you are in.
To explain this better, here's an example level:
http://www.student.kun.nl/rvanhoorn/Optimization/room1.JPG
When vbps cuts this map in leafs, it will only look at world brushes, that's why I've left entities out in the example. Displacements also aren't used. We'll run this map through vbsp and take a look at how its done:
http://www.student.kun.nl/rvanhoorn/Optimization/room2.JPG
This picture shows all leafs in this map. A total of 5. Now vvis will calculate which leafs can see which leafs:
green can see blue and red blue can see green and red red can see all other leafs yellow can see purple and red purple can see yellow and red

All this info is stored in the mapfile, and you are ready to run the level. Now, when you are in the green leaf, the engine only has to draw the leafs the green leaf can see according to vvis' calculations, in our case only blue and red. However, if you are in the red leaf, the engine still draws the entire level (because all leafs can be seen by the red leaf), no matter where you are in the red leaf, even if you wouldn't be able to see parts of it from your position.
When even the smallest part of a leaf is considered visible from even the smallest part of a leaf the player is in, that entire leaf will be considered for rendering ingame. Remember, everything in a leaf belongs to it, so whether it's a brush, entity, model or light, it will only be drawn if the leaf it is in is thought to be visible.
If you want to know how YOUR map is divided into leafs, you'll have to do the following:
Play your map and type "mat_leafvis 1" in the console (type "sv_cheats 1" first). A wireframe box will be drawn showing the current leaf. As you move around in the level, the box will redraw each time a new leaf is entered.
Fun as that is, it wont help you much. You want to see each leaf in the entire level. To do that, you must use a special option for vbsp, and a special program accompanied by hammer.
In expert compile mode make a new configuration, called glview (this allows you to use it for all your maps), and create the following commands:
Executable | parameters |
$bsp_exe | -glview $path\$file |
glview | -portals $path\$file.gl |

Make sure both commands are set to run, and press GO!
This is what you see with the example level: (It's normally only black & white) You can use your mouse and the WASD-keys to move around your level when in Glview:
http://www.student.kun.nl/rvanhoorn/Optimization/room3.JPG
As you may see, vbsp has cut up the red leaf in two halves. (Along the brown face) This is because vbsp always cuts your level up at the green lines in the editor (and every 1024 units (= red/brown lines). You can choose to highlight these lines under [tools->options] tab 2d windows and select the highlight every...and enter 1024 units. If you look very careful in the first pics, you can see the green line at the place of the brown cutting.
But is the cut helpful? I could have moved the entire level, so vbsp didn't cut the red leaf in two, but obviously I didn't. (just to teach you a lesson ;) )Well, to tell you the truth, it doesn't help our performance. Since all leafs seeing the red leaf can also see both halfs of it separately. With minor adjustments, however, this can be changed: (white lines mark visibility)
http://www.student.kun.nl/rvanhoorn/Optimization/room4.JPG
In this case, green can see blue, red and brown. Just like when red was still one big leaf. But, take a look at purple (the upper room). It cant see red, and therefore, one less leaf has to be drawn from purple. And vice-versa, when a player is in the red leaf, the purple leaf doesn't have to be drawn. From the other leafs no optimizing has been done, but purple and red have one leaf less to be drawn.
This is just an example of when smaller leafs have advantages over bigger leafs.
But, how can we force vbsp to cut a leaf in two? That's where we use HINT-brushes.
Here's how hinting works. First, you create a brush, with on all sides the "tools/toolsskip"-texture. (this texture makes those faces to be ignored by the compilers). Now, wherever you want a leaf to be split, create the "tools/toolshint"-texture. The leafs are split EXACTLY where the face with the hint texture is. You don't have to tie the brush to an entity.
You can also take a look at the example map made by valve in: "sourcesdk_content\hl2\mapsrc\sdk_hints.vmf" for more information on how to make these hint-brushes.
As an example, you can take a look at how I can cut up the red leaf in the above level:
http://www.student.kun.nl/rvanhoorn/Optimization/room5.JPG
(note: only the white face of the brush is HINT, the rest is SKIP. Also note that in the 3d view of hammer both textures are transparent at default)
Be sure to fit hint brushes to walls exactly, or else their effect *may* be lost. It will also make your level tidier.
An example to look at is a corner. Normally, nobody can look around a corner, unless he had x-ray vision. Therefore there's no reason to draw stuff that's around a corner. Lets see how we can improve a corner between two very complicated "rooms":
http://www.student.kun.nl/rvanhoorn/Optimization/room6.JPG
Each room has four pipes, offcourse they are func_detail, or else vbsp would have cut the level in dozens of leafs! (see func_detail) Not to mention all the cut-ups the pipes would have caused on the floors and ceilings. vbsp can cut the level using the brown, the pink or both lines. In any case, both players (the green boxes) can see everything, because the leafs they are in can see each other. That's therrible!! The engine has to draw all cylinders at once (yes, it can easily handle 8 cylinders, but just imagine each cylinder being something very complicated and big), while we can avoid that and increase FPS, simply by placing a HINT-brush:
http://www.student.kun.nl/rvanhoorn/Optimization/room7.JPG
By placing a hint-brush at a 45 degree corner (crossing the corner exactly), we have forced vbsp to cut up our level in theses three leafs. If we go look at our visibility table:
Red can see green, BUT CAN'T SEE BROWN Brown can see green, BUT CAN'T SEE RED Green can still see everything
We can see that neither of the players can see both rooms now, unless they are in the green leaf. But that is supposed to happen, because in the green leaf the player can see both rooms directly at once. Wow, look at the optimisation we have done! We could have also placed the hint at another angle, as long as it touches the middle angle it stil works. Remember, two leafs are only visible when vvis can draw a straight line from leaf to leaf without crossinh world-brushes?
You may think that this green leaf is still very difficult to render. And you are correct. We cant solve that using hints. We can, however, try to make sure both rooms can never be seen from the same point. To do that, we need to add more corners, to make sure vbsp will never make leafs in a way both rooms can be seen from one point, for instance by using an S-shaped hallway.
Let's examine a few more examples:
http://www.student.kun.nl/rvanhoorn/Optimization/rooms.JPG
We see here a few examples of when, and how to use Hints. In all cases we try to hide the green players from each other. The upper three work as they should. The left one illustrates that it doesn't even matter if its one hint-brush spanning the corner, as long as its angle is > 180 degrees the two players will not render each other, because no straight line can be drawn betwen their leafs. The bottom-left example shows you that can happen when the angle is less than 180 degrees. One can easily draw a straight line from leaf to leaf, resulting in non-functional hints. When the corner is 180 degrees, as in the middle top picture, you can suffice placing two hints like there. In fact, you can even make them closer to the players, but that would increase their effectivity (more stuff will be drawn "around the corner". ) see the example below the middle. Because you lowered the hints, vbsp is forces to cut up the top visleaf because VISLEAFS CAN'T BE CONCAVE (hollow at any side). No matter how vbsp cuts up this corner, the hints will always work less effective AND create extra leafs (more work for vvis). In our example, the left player can see leafs 1 and 2 (same surfaces as above), but the right player will see all three numbered leafs (which is more than the top example.). The two right pictures show you you should keep things as simple as possible. If you can suffice using one hint, like in the top example, do so. And, don't use hints if you don't need to, like in the bottom picture. there is absolutely no way vbsp can create leafs that enable both players to see each other, so there's no need to use hints here to hide the players from each other (you can offcourse use hints to hide parts of the halls). In the same example you can also see a nice wall (the middle one) that should NEVER be a func_detail: because func_details don't exist according to vvis, it will think both players will see each other and thats not what we want. Vbsp may even make it so both players here are in the same leaf.
Another example is THE WALL. (also demonstrated in sdk_hints.vmf)
http://www.student.kun.nl/rvanhoorn/Optimization/room8.JPG
Meet Weebl and Bob. Weeble and Bob can see everything the engine renders, but are very sad, because they can see those ugly pipes on the other side of the wall. Even though theres a wall, that is supposed to hide the pipes, they can still see the pipes. It's because vbsp did a piss-poor job:
http://www.student.kun.nl/rvanhoorn/Optimization/room9.JPG
The leaf they are in, can see the other leaf, and thus it is drawn. Too bad. But, luckily theres a cure: a hint-brush.
http://www.student.kun.nl/rvanhoorn/Optimization/room10.JPG
there, the red-box with a hint-texture at the bottom face (white line) forced vbsp to leaf the level correcly. Weebl and Bob now can't see the pipes anymore, since their leaf (brown) can't see the leaf with the pipes (yellow). w00t for them!
The example with Weebl and Bob is exactly the same as the top-middle one above them, only in another dimension. But it seemed so different. Yes, i know. go cry if you wan't, but hinting is never easy. for simple maps it will, but i've never seen simple maps. All these examples are 2d images, BORING! When it comes to 3d, and your rooms are more complicated than 2 halls of the same size, things will start to get hard. Especially with outside area's, you must be really careful to use hints optimally. Trial and error is the only way. place a hint where you think its placed best, and compile, see if it made any difference. you may even choose to look at your level in glview to see what your hint has accomplished. Its all up to you. Good luck with whatever you are trying to optimize, you probably need it.
Offcourse, as I already mentioned, use func_detail wherever possible to simplify the level so you know better where to place them hints. You can also try to place hints in doorways to stop them from accidentally cutting up the rooms they connect:
http://www.student.kun.nl/rvanhoorn/Optimization/hint.JPG
On the left, our doorway (red) has cut up the rooms, resulting in more leafs than neccesairy. So, we add a hint brush as on the right and we have three simple leafs again, as they should.
Func_occluder
The func_occluder hides entities from you, simple as that. It can be turned on and off by triggering it to do so. Great you say! Well, no, the picture isn't only good. Some bad point:
- func_occluders work real-time. That is, while playing your game, the engine has to decide which entities are, or aren't blocked by any func_occluder. This requires some calculating power, therefore you have to make them worthwhile: a func_occluder has to hide at least a few (more or less) complicated models to be effective. Failing to do so will lower FPS, because the cost of the func_occluder becomes higher than its profit. To check, run your level and use "r_occlusion 1" and "r_occlusion 0" in the console to see where you get the least load. If the latter gives you higher FPS, ditch your occluder, otherwise keep it. It is as simple as that.
- func_occluders only block entities, not brushes. If you want to block brushes as well, use an areaportal instead. Also, shadows (even of blocked models) are still drawn.
Using r_visocclusion (again 1 is on, 0 is off ) you can see your occluders in action:
Red means entity is drawn. Green means entity is blocked (not drawn). White lines denote active func_occluders. Entities that aren't affected by occluders have no border.
Func_occluders don't need vis or rad to be run, and work on maps with leaks too. They can cause erros when touching a func_areaportal or when being in a brush that touches more than one area. (Warning: Func_occluder straddles multiple area's.) An area is defined as a part of your map sealed of by an areaportal.
An example of usage is (like in my example map) a func_breakable, which blocks vision of models, but once its broken it deactivates the occluder so the player can see all the models like he should:
http://www.student.kun.nl/rvanhoorn/Optimization/occluder1.JPG
Finally, how to make them:
Wherever you want a func_occluder, make a brush, texture it with the NODRAW texture, and, at any side you want it to occlude entities (the side the player has to be to occlude the entities on the other side) place the tools\toolsoccluder texture. You can texture multiple sides with it, but be careful not to use on other faces if it isn't neccesairy, because each face adds to the cost of the occluder.
http://www.student.kun.nl/rvanhoorn/Optimization/occluder2.JPG
If the func_occluder here (light-blue) would be active, and the white-marked face would have the OCCLUDER texture, most of those prop_physics would not be drawn. I could texture the other sides with occluder too, but I know the player will never get at those faces (the player will never get into the room with all the props, and he cant clip through the walls) so that would just mean extra engine load.
Tie the brush to a func_occluder, and give it a name. Now you have three options:
Set its starting state (start active, e.g. start blocking stuff, or start inactive)
Then you may, if you need it to, trigger it with either a "activate" or "deactivate" input to start or stop the entity from working.
You can also choose to leave it at its starting state.
Take this example:
http://www.student.kun.nl/rvanhoorn/Optimization/displacement.JPG
The small red box is the player, the green curved line is a displacement, the brown box is a func_occluder and some models are on the other side of this hill. Since displacements are discarded by vvis, it presumes the player can see through them. (this also means large amounts of displacements don't affect vistime). That would mean our player in the above picture would have all of those models drawn, even though he cant see them. That's why we can put a func_occluder in the hill. With the black side being the side with the OCCLUDER-texture, the player will not have all those models drawn as long as he has to look through the black face to see the models. If there were also lots of models on the players' side, we also have to put the OCCLUDER-texture on the other side of the fun_occluder, so the player doesn't see them when he's on the other side of our hill. Of course this func_occluder is set to close at all times. As always:
- Make the occluder as small as possible. Larger occluders usually occlude more, but also cause more calculating power for them.
- For optimal occluding, stretch your occluder from the ground below the displacement (in this case a nodraw-brush) to the top of our hill. This doesn't mean its most optimal however when its maxed out.
- Only texture those sides with OCCLUDER which the player has to see through to see the models you want to block. For instance, it would be stupid to texture the top and bottom with the "OCCLUDER"-texture.
The example map from valve about occluders can be found here: "sourcesdk_content\hl2\mapsrc\sdk_occluders.vmf"
Func_areaportal
The func_areaportal is essentially the same as the func_occluder, only more powerful. This lies in the fact that the areaportal hides entire leaves, including all entities and brushes they contain. They require less real-time calculations than occluders, and are more powerful. Therefore, usually, the func_areaportal is a better solution than a func_occluder. However, the areaportal needs more careful planning.
Creation
- Create a 1 unit thick brush (like a normal door) in any passageway. It can be thicker if you wish, but make it as thin as possible if you need to fit it inside a door or window. Be warned though: Both large surfaces must touch, and completely seal off a doorway between leafs. If you fail in this, vbsp will generate a leak error, so make sure the func_areaportal is surrounded by world brushes, (not func_details) on its small faces (just like a door is surrounded by walls).
- Texture it with the tools\toolsareaportal material on all sides.
http://www.student.kun.nl/rvanhoorn/Optimization/areaportal1.JPG
Areaportals also can't pass through water-surfaces. If you still need them to, just make two areaportals, one inside the water and one outside, each touching the other exactly on the water surface.

You can control the areaportal in two ways:
- By setting the "name of linked door" to a door, the areaportal will open/close as the door would. Make sure the areaportal is INSIDE the door, or else the door itself won't render when its closed!
- By triggering the func_areaportal with a Close or Open input from another entity, e.g. a trigger_once or logic_relay. Or from a door if you have 1337 skills. (There's even a special option for that, namely the "Linked door"-property. Just put the name of your door there and it should work.
It doesn't matter whether you use a model or a brush-based door: As long as the bounding box of the model is on both sides of the areaportal, it will be drawn.
http://www.student.kun.nl/rvanhoorn/Optimization/areaportal2.JPG
Notice that func_areaportals must be the only direct way between its two areas, or you may get leak-errors (The pointfile will point you to the right place luckily.), or the areaportal will simply refuse to work. So if you've got two ways between two areas, make sure the other door has a func_areportal too. You can also use func_areaportalwindows for this. Since areaportals may span through brushwork, it's also possible to use one areaportal for two doors by stretching the areaportal brush across both doors, if they are next to each other.
Spaceship Example
If we go back to our spaceship example, where the entire map is a ship and all entities are humans, these areaportals are the airlocks in bulkheads. They make sure that when all of them are completely closed, the part they close off is not drawn. Not even when you are supposed to see it. It's just like that part of the map is gone.
http://www.student.kun.nl/rvanhoorn/Optimization/areaportal0.JPG
Look, its the USS Optimize! Ready for the most hazardous space-adventures!
If the green box is the player, and the thick black walls would be normal worldbrushes, the player would see all parts of the ship, unless some expert architect made walls and corners and hints to prevent that. But, when we close a part of the ship off with closed areaportals, that wouldn't even matter. The bow/bridge is closed by two closed (red) areaportals. The bow is now completely separated from the rest of the ship (the part where the player is) so it wont be rendered at all! The other two parts of the ship, the engine room and my bedroom, will be rendered because those area's of the ship are not sealed from the player by world-brushes and closed areaportals. (Any part of your map closed off by world brushes and areaportals is called an area. Our spaceship would have 4 areas.) My bedroom and the area the player is in, are actually one area, because the areas aren't sealed from each other. (One of the entrances doesn't have an areaportal.) Vbsp may give a leak-error because of it, or the single remaining areaportal simply would work, just like an airlock won't work if there's a big hole in the hull right next to it. The engine room is rendered because not all airlocks are closed. Remember, all areaportals have to be closed if you don't want the "other side" to be rendered.
As a last example, when you want to use an areaportal to hide the inside of a house with areaportals, you don't only put an areaportal in the door, but also ones in the windows, on all floors and even in the chimney if it is an open access.
The purpose of this all is that areaportals create areas, sections of your map completely closed by areaportals and world brushes. When all areaportals to a certain area are closed, that area will not be drawn. If you fail in completely closing in an area with world brushes and areaportals, you will get errors like : "WARNING: areaportal doesn't touch two area's". They wont work either.
Another property of func_areaportals is the fact that they cut adjacent visleafs:
http://www.student.kun.nl/rvanhoorn/Optimization/areaportal3.JPG
This can be handy for instance when you have a small hallway coming out into a big room (e.g. outside), but sometimes it isn't useful and you would want to use hint brushes to stop this from happening.
Areaportals also hide leaves occasionally, so even an always open areaportal can increase the speed of your map!
Areaportals in multiplayer maps
Areaportals in multiplayer maps are somewhat less effective. If, for instance, you have one controlled by a door, theres always some bloke that opens the door but doesn't close it afterwards. (I cant blame them, since there's a war going on.) This effect is even worse if you need more than one areaportal to close areas in your map.
The simple (yet annoying) solution to this would be a door that closes after being open for some time. Sure, it cuts down on realism but it will help you cut down lag when players don't close the doors again.
To make a door shut after a certain time, enter the time in the "delay before reset" keyvalue in your door (func_door_rotating or prop_door_rotating or whatever entity you use).
Also try to replace func_areaportals with func_areaportalwindows as much as possible (if possible), since they work clientside (e.g. they can be open for player 1 while closed for player 2).
If you don't do this, you may have a map that runs fine at first, but becomes worse and worse to play after a (certain amount of) doors that control areaportals are open.
Func_areaportalwindow
The func_areaportalwindow is a special func_areaportal. Instead of just being open or closed, it shows the leaf behind it if you're close enough, and if you move further away it will display another texture (e.g. a dark window texture) instead of the entire leaf. Most rules for areaportals also apply for areaportalwindows.
The areaportalwindow is controlled with the fade start and fade end distances. Starting at fade-start distance, the leaf on the other side will be faded out and the window (brush or model) will show up, more and more, until you have reached fade end distance, where the window becomes non-transparent and the leaf behind it invisible. The effect is best used on small windows (less eye-catching), and it may take some tries to find the correct fade-distances for you to use, but once you got them working, they are great for optimizing purposes.
Creating a window portal
To use them, again make a brush with the tools\toolsareaportal texture (for reference only, they seem to work without the special textures) and tie the brush to the func_areaportalwindow entity. Again it must touch world geometry at all sides, and has to be the only way between two leaves (that, or another func_areaportal(window)).
Alter the translucency limit as you wish (It prevents the window-brush from becoming too transparent when your closer up, if translucency limit would be 0, the window would become invisible too.) and also change the fade distances to your wish. Next, create a window at the portalwindow, make sure its bigger (err, thicker) than the areaportal, like so:
http://www.student.kun.nl/rvanhoorn/Optimization/areaportalwindow.JPG
I've now selected the func_areaportalwindow. In green is the window, and in blue are all the world-brushes. The window in my case is a glass texture one one side, and nodraw on the other sides, the usual way to create windows in HL2. Now tie the window to a model (e.g. func_brush) and name it. Go back to the func_areaportalwindow, and enter the name of your windowbrush in the "rendered window" area. And you are already done.
You can experiment using models instead of a window, or even multiple things. You can also try to create your window out of multiple entities, one that doesn't have to fade out (like the wooden parts of the window) and another texture for the actall glass. Only add the entity that is supposed to fade in the "rendered window" area of the areaportalwindow offcourse, the other entity may just as wel become a func_detail.
Lastly an ingame shot of the areaportalwindow in action: (near, further, furthest)
In the last screen, the room with all the barrels isn't drawn anymore.
http://www.student.kun.nl/rvanhoorn/Optimization/areaportalwindow1.JPG
(See the example map on the bottom of this page for an example.)
Another example is has the same purpose as the func_occluder in the hill. Take this example:
http://www.student.kun.nl/rvanhoorn/Optimization/areaportalwindow2.JPG
We could make it so this areaportalwindow closes when the player is far away, since he won't have to see the other side of the hill unless he is on top of the hill.
The nice thing about areaportals, is that when you have a skybox in your map, closed areaportals also show skybox. So if we would close this areaportal (purple) the player will see skybox! ...which he would also see without the areaportal. You can also throw grenades through the closed areaportal, as if it wasn't there.
An example (See also example map.)
http://www.student.kun.nl/rvanhoorn/Optimization/test.JPG
Notice how you see (should see) exactly the same if we didn't have wireframe on, only to the right the boxes are not rendered because the areaportal was closed. For the rest, it works just like the previous window, just make sure the window brush is non-solid if you plan on it being passable.
The only problem with this method is that you need to make an area of "the part behind the hill", so that may be a problem for complicated outdoors. Still, its probably better than nothing.
Console commands
How can you see how much/what is rendered? Well, you can only see it in-game.
So, go compile your level (don't use a fast vis; it won't work optimally and it will give you a wrong perception as to what you have to change) and go to the console. First type "sv_cheats 1" (without the quotes). This is necessary for some of the commands to work because they can be used as a cheat, as if you wallhax0rs didn't know. ;-)
Useful ones are:
mat_wireframe 1
Shows everything the engine draws, including models, but only the outer lines (toggle off with "mat_wireframe 0") Therefore, you can see through walls to see EXACTLY everything the engine draws. If you were Weeble or Bob, you wouldn't see the pipes without mat_wireframe on.
Mat_wireframe is also useful to check if and how your occluders and areaportals are working.
+showbudget
Shows engine loads for different parts of the engine, e.g. models, worldbrushes, ropes, decals etc. (toggle off with "-showbudget") see "showbudget" for more information.
mat_leafvis 1
As mentioned earlier, this shows the boundaries of the leafvis you currently are in (toggle off with "mat_leafvis 0)
cl_showfps 2
Shows fps and network traffic. Great for checking the impact of physics-objects on network traffic for instance.
Other command useful for checking what exactly causes FPS drain. Use these in combination with a FPS-meter, e.g. "+showbudget" or "cl_showfps 2" to see the impact.
mat_showwatertextures 1
Shows which entities are drawn twice because they are partially inside water.
r_occlusion 0/1
Toggles func_occluders on and off, to see their effect.
mat_bumpmap 0
Turns off bumpmapping, so you can see what kind of impact is has on your map ("mat_bumpmap 1" to put it back on)
mat_specular 0
Turns off reflections, so you can see what kind of impact they have on your map ("mat_specular 1" to put it back on)
+showbudget
Showbudget is a useful tool to determine which resources actually make your map lag:
http://www.student.kun.nl/rvanhoorn/Optimization/budget.JPG
along a small FPS-indicator, you'll see this screen appear. Above is a graph of the total amount of FPS, under that are indicators for the lag created by each group
Here's a list of the groups, and the usual cause of lag created by them:
Unaccounted
Lag caused by other programs (e.g. virusscanner, winamp, hammer). Shut them down if the lag is too big.
World rendering
Lag due to rendering of worldbrushes. To remedy this, use hints, or func_areaportal(window) to hide them. You can also use an env_fog_control to set the max rendering distance (clipping distance) and use the fog to hide the clipping. Just reducing or simplifying brushes works too offcourse.
Displacement rendering
Lag due to dispacements. Counter it by reducing the amount of dispacements, or set their "power" to a lower number. Making sure you cant see the leafs they are in helps even more, so you can also use hints or func_areaportal(window)s
Game
Any lag caused by logic_entities and other such basic calculations. Removing them is the only option, be sure to not use more logic_entities than nessesairy! ( thanks to klaus viehöfer )
NPC's
Not sure either, can be model-rendering or AI-handling or both
Server Animations
Unknown (by me)
Client Animations
Unknown (by me)
Physics
Lag due to all the physics. If this lags too much, either remove physics-objects or try to switch some to multiplayer-props
Static prop rendering
Lag due to static props. This can be remedied by using hints, areaportals or occluders to hide the entity, or use its fade-properties (turn on helpers in hammer, they are the two circles, one for fade to start, the other for fade to end and make the prop invisible. Between fadestart and fademax theres no performance-gain, youll gain only outside the fademax) you may also try to convert small (unimportant) ones into prop_details
Other prop rendering
Same for static props, only for all other props (eccept detail-props). Only be warned that if entities can move, small fademax's may make them invisible when they shouldn't.
Light cache
I think this is the cache for prop-lighting. If it is, reduce it by reducing props, or the amount of lightstyles on them.
Brush model rendering
Lag due to brush-based entities. Solve it just like world-brush lag
Shadow rendering
Lag due to rendering of prop-shadows. Remedy this by disabling shadows, remove props or use hints/areaportals. Occluders don't work for shadows, strangely enough
Detail prop rendering
Rendering of detailprops (prop_detail is an entity that is only drawn if your machine can handle it, therefore it should only be used on small, non-essential models). To remedy, see static props.
Particle/effect rendering
Lag due to any particles you have, e.g. dustmotes, things coming from env_rotorshooters. Remedy this by reducing the number of particles, or their life-length. This shouldn't be a problem for most people.
Ropes
Lag due to ropes, can be remedies by reducing the ropes or by decreasing the number of subdivisions. I'm not sure, but I guess moving ropes cause more lag than non-moving ropes, so disabling wind for instance may gain you something
Dynamic light rendering
Lag due to dynamic lights. Dynamic lights are quite a heavy load for the engine, and so should only be used sparsely. Also, the point_spotlight has the "no dynamic light"-flag off by default. Make sure you turn in on to reduce some serious lag.
Networking
Lag due to the network. To remedy, reduce the network-traffic your map generates (prop_physics->prop_physics_multiplayer may help) by removing any unneccesairy entity. I don't know why, but when your playing your own map on your own pc (no network involved) there's stil some lag. I guess even then HL2 is talking to the steam-servers
Sound
Lag due to sounds, remedy by reducing the sounds in your level
VGUI
Lag due to Versatile Graphical User Interface (yes, I googled). I think its due to HL2 menu's, HUD and console (and the showbudget-screen!)
FileSystem
This is due to lag in retrieving info from models\sounds\etc from your harddisc. Also due to saving games. Try defragmenting if this lags your map.
Prediction
Lag created by a system that tries to predict certain things. My guess is that these things are mainly player/monster/physics movements that make those objects to have smoother movements.
Interpolation
My guess at the moment is that Interpolation has something to do with physics. Ive made a test-map, and saw this value go over the top when I rpg'ed two hundred physics barrels. I think this predicts the actions of physics before they actually happen, or calculates trajectories of projectiles.
Swap buffers
This can be anything, from water (try reducing it to cheap water if it is) or fire, smoke or steam, glass or other transparent textures. Apart from reducing visibility of the leafs, try to reduce their quality or quantity, but only if it really gets out of hands. Also, if you have it enabled, bump-mapping is also a cause. In my personal experience, lots of reflective surfaces also increase swap bufers lag (especially the combine metals).
AINET
Probably has to do something with AI networks. E.g. info_node paths and pathfinding.
Occlusion
Cost of func_occluders and maybe areaportals too. Solution: remove the occluding entities, make sure they gain more than they cost!
Overlays
Lag due to overlays and maybe even decals. Large ones cost more than small ones, overlays cost more than decals. Removing a few might do the trick.
CLagCompensationManager
Apart from the name (Lag Compensation Manager) I don't know anything about this value. Maybe this is a program that tries to make the game fluid even though theres lag (e.g. physics-stuff?) Reducing lag should help I guess.
Cview-Render::render
No idea
3D Skybox
Lag due to anything in your 3D skybox. Reducing stuff in it is the only way to battle this.
If all else fails
A few useful things to consider using when your map is still lagging to much is the use of:
- env_fog_controller The env_fog_controller is an entity that makes fog, but its most appreciated function is the fact you can set a maximum visible distace. In other words, everything more that so many units from the player will not be drawn. The fog can be used to mask this clipping, so players will think you made a nice map with fog for a nice effect, but actually you were trying to hide your bad optimizing skills...Just place the env_fog_controller anywhere in your map, and enter the "fog start" and "fog end" values. Everything farther away than the "fog end" value will not be drawn.
For optimal effect, make sure the primairy fog color is whitish (its always like that) and that the primairy color is more blended with your skycolor. For instance, if your sky is yellow, make the secondairy fog color yellow-whitish, and if your skycolor is blue, make the secondairy fog color blue-whitish. Experiment to find the values that suit your map best.
Also, when you are using a 3d skybox, make sure you set the same fog properties in the sky_camera entity, or youll see strange effects with near buildings being fogged and far away buildings being completely visible. - fade-properties When you have helpers turned on in hammer (its the diamond shaped button on the top) you'll notice entities get circles. You can use these circles to denote at which distance your model entities should fade out (inner circle) and disappear totally (outer circle). These circles correspond with the "start fade distance" and "end face ditance" properties of the model-based entities.
Few words of warnings:
- If you set the fade-distances too low, the object wouldn't be visible when you should see them. This also happens when you accidentally select the circles instead of something else and make then really small. Because of that its best to hide helpers as long as you don't use them.
To reset the distances, go to the properties of the entity and make sure "start fade dist" equals -1, "end fade dist" equals 0 and "fade scale" equals 1. - Static entities can be invisible when the player is out of its area (and out of the room) but when dealing with moving entities remember they may be brought to open area's where they can't be visible while they should.
- You only gain performance outside the "end fade dist", fading entities cost just as mch as normal ones.
- On machines running DirectX level 7, the props will fade earlier than the values set in the entity to further improve rendering speed. This can be controlled on a per-MOD basis by creating a "dxsupport.cfg" file for your mod and specifying the values for the console variables "cl_detaildist" and "cl_detailfade" for the various dx support levels. (taken literally from www.valve-erc.com)
- If you set the fade-distances too low, the object wouldn't be visible when you should see them. This also happens when you accidentally select the circles instead of something else and make then really small. Because of that its best to hide helpers as long as you don't use them.
- func_lod A func_lod is a brush-based entity that brings fade-distances to brushes. Just like the fade-distances does on models, this entity allows you to make brushes that disappears after a set distance. Great for outdoor optimisation of details.
This is a real entity, so it won't seal leaks, and it costs more to render than func_detail. - Lightmaps To make VRAD take less time, you can increase the lightmap size. VRAD divides each face it has to light in squares, and then calculates for each square how much lighting it needs. Afterwards, all squares are faded into each other to create a fluent effect.
Reducing the size of these squares increases VRAD-times and qualitiy of lighting, increasing the size does the opposite.
You can specify the size of the lightmaps on a face by using the texture application tool. To the right of "texture shift" you'll see a box where you can input your custom lightmap scale. Default is 16 (units per luxel). Increasing this number will decrease VRAD-times, but also decrease lighting quality.
Because larger lightmaps mean less quality, only lower them on faces that are equally lit and have no shadows falling upon them, faces players can hardly see (for instance behind a model or far away faces). Also see the example map from valve about lightmaps: "sourcesdk_content\hl2\mapsrc\sdk_lightmaps.vmf"
Optimizing outdoor area's
This troubles a lot of people, and thats obvious. One can use hints if he has world-brushes to hide the leafs behind, but usually you don't have that on oudoor area's.
So, what can we use? Here's a list of items I discussed and I think you can use:
- env_fog_controller Even though you may not like fog, it does help performance a lot of used correctly (that is, with the clipping distance set not too high)
- func_lod Especially if you have some smaller brushes, these are great to use instead of func_detail.
- Prop fade-distances If you got some smaller props you don't care to disappear at a distance. You may also use this for bigger props, as long as you make sure their disappearence stands out. You may also check the area where a certain prop should be seen, and use the fade-distance to make the prop visible inside this area, but not in others.
- Decrease power of displacements used
- func_occluders can be placed inside hills or props to occlude props on the other side of the hill.
- func_areaportalwindows can be used for the same purpose if you have a skybox (2d or 3d). (see that chapter)
- Add worldbrushes If you can, add world brushes so you can use them in combination with hints
- prop_detail Not only for grass, try them!
If these things don't help you, you may need to alter your map for optimization. I can't help you with that, I can only recommend using hills, buildings, or props so you can hide whatever is behind them.
Example maps
All of valves example maps can be found here:
Your steam drive:\your steam directory\your steam username\sourcesdk_content\hl2\mapsrc\
The example map for this tutorial can be found here