Valve Developer Union

Controlling Visibility With Areaportals and Occluders

(September 30, 2017)


Visibility control is the most important part of optimizing your map. Too much happening on-screen at once is guaranteed slowdown, so cutting the amount of detail being rendered is key. While the second compile process, VVIS, does a rather good job of this on its own, it's not perfect, and occasionally, it needs some help.

There's two types of brush entities you can use to control what's being rendered in an area: areaportals and occluders.

Why should I use areaportals?

The VVIS compile process determines which visleaves (empty spaces inside the game world split by brushes) can be seen from any one point, and normally, this is more than enough to control visibility in a level. If you're not at CT spawn on de_dust, why would the game render it? There are special cases where this isn't enough, however. Take the following sample scene.

Two rooms connected by a hallway
Two rooms connected by a hallway

The issue with it might not be immediately obvious, but switch on mat_wireframe and it'll become apparent:

The second room is rendering several props the player can't see
The second room is rendering several props the player can't see

That's right, we're rendering a bunch of props and NPCs that aren't actually on-screen. Because of the way these areas are set up, both rooms and everything inside them will get rendered, no matter where the player is in either. What's the solution? VVIS won't help in this instance; the hallway visleaf touches both rooms, which means both rooms are in the potentially visible set at all times. Enter the areaportal.

What are areaportals and how do I use them?

The areaportal is a brush entity that tests for what the player can see through it at any given moment. To make one, make a brush with the tools/toolsareaportal texture on all sides, tie it to a func_areaportal entity, and put it at the mouth of a hallway.

Creation of an areaportal
Creation of an areaportal

The areaportal can either be open or closed. If open, the areaportal will prevent any entity not immediately in sight of the player's viewpoint (call it a view frustum to impress your friends) from being rendered.

An open areaportal tightly culling visibility
An open areaportal tightly culling visibility

If closed, the areaportal prevents anything from being rendered through it. You can also link an areaportal to a door entity through the "Name of Linked Door" keyvalue, and that areaportal will open and close with the door. Be sure to make the areaportal thinner than the door so it does not block the door itself from being rendered.

An closed areaportal blocking visibility altogether
An closed areaportal blocking visibility altogether

Portals in the I/O system

d1_trainstation_04's rooftops and Valve's use of areaportals

Areaportals can be open and closed using Source's I/O system, and triggers can be used to manipulate them independent of doors. Valve has used this setup in their own maps to block rendering of certain sections of the map altogether. A good example of this can be seen in d1_trainstation_04, the last map of the Point Insertion chapter of Half-Life 2. An areaportal is used to block the entire section of the map where Gordon is intercepted before it's visible to the player. This is opened with a trigger. As the rooftops are open, VVIS alone wouldn't block it off, while a closed areaportal would.

Care and feeding of your areaportals

Simple, right? Link an areaportal to every door and let it go.

Of course, it's not that simple. It takes valuable cycle time for that areaportal to function, and if there's too many on-screen, rendering the scene without them actually becomes faster. Like all optimization techniques, you'll have to test it in your specific scene and see what works best. For example, the spawn room has nothing in it, aside from the player. Thus, an areaportal facing the spawn room is nonsensical.

There's a few caveats to working with an areaportal. If your areaportal crosses the surface of a water brush, you'll need to split it at the water surface and make the split two separate areaportals or else the water brush won't display right. (They'll merge back together at runtime.) The other major caveat involves "leaking" areaportals and how they seal off areas.

Areaportal leak !
Brush 1269: areaportal brush doesn't touch two areas

By this point, you should be aware that a map with a leak is bad. VVIS won't run on a leaky map, and water and particle effects won't display right on a leaky map, either. Areaportals also need to be sealed, in the sense that any one side of the areaportal shouldn't be able to touch any other side of the same portal. If the compile tools can find any way to touch one side to another, it'll leak. This becomes the most tedious part of working with areaportals.

You can think of an areaportal as splitting areas of the map into smaller maps. The inside region of any one of the smaller maps shouldn't touch the inside region of another. Make sure that all sides of the areaportal touch solid world geometry; func_wall and func_detail will not stop an areaportal leak. If it's not touching all four sides of the hallway, you'll know because your pointfile will be rather short.

A leaky areaportal that doesn't touch four walls
A leaky areaportal that doesn't touch four walls

If your areaportal leaks and you're sure it's not because it was constructed wrong, use the pointfile to find the leak and plug it up with another areaportal. Keep doing this until it stops leaking. Remember, if you have too many in one area, it'll be faster to render without them.

A leaky areaportal that doesn't block both hallways
A leaky areaportal that doesn't block both hallways

Occluders

Occluders work in a similar way to areaportals, but are more niche in use. Occluders will block any props behind them from being rendered. Occluders don't block world geometry, split visleaves, or cause leaks. In fact, an occluder can be completely free-standing, with no ill effects. To make an occluder, create a brush, texture all of its sides with tools/toolsoccluder, and tie the brush to func_occluder.

An occluder blocking props behind it
An occluder blocking props behind it

Note the white box (that is, the occluder surface) and how only the barrel peeking out from the side is rendered. Occluders have their own render cost, so use them sparingly.

Tip

Debugging areaportals and occluders

Console commands can help you test the effectiveness of your areaportals and occluders. mat_wireframe 1 was used at the start of this guide, and it'll show what brushes and models are rendered at present, even through walls. Occluders can be tested with r_visocclusion, which draws boxes around any props in the scene. Green boxes are being occluded, red ones aren't. You can see its use in the above screenshot.

The downside to areaportals and occluders is that, because of their effectiveness versus their cost, their use is rather limited. Trying to fit areaportals into large, open areas of your map can be done, but it's best to just reduce detail and use open areas sparingly. Areaportals are a helpful tool, but they don't work miracles.