SteamVR/Environments/Adding Lighting

From Valve Developer Community
Jump to navigation Jump to search
Todo: Verify everything. No official documentation is currently available.

The Renderer

The Source 2 engine appears to be designed with multiple different renderers in mind. SteamVR Home uses a forward renderer tailored for VR applications, while Dota 2 uses a deferred renderer with real-time global illumination.


The renderer in SteamVR Home supports a combination of two kinds of lighting, real-time direct illumination and static baked global illumination. The real-time illumination uses point light sources and deferred shading with cascaded shadow maps. The global illumination uses path tracing[confirm] to generate lighting which is baked to lightmaps and light probe volumes. Image-based lighting is also supported. An alternate baker that uses photon mapping to bake vertex lighting and light probe volumes is also available.


The lighting can be split up into four different groups, diffuse and specular, with each with respectively direct and ambient lighting. Physically based rendering (PBR) is assumed to be used, which among other things means that surfaces are energy conserving and cannot emit more light that they receive. The light received by the surface is split up into diffuse and specular reflections according to the material properties.


Direct diffuse light can either be rendered by real-time lighting or baked into the static lighting.

Indirect diffuse light is baked into the static lighting.

Direct specular light is emitted by direct light sources, and is rendered on surfaces with specular materials.

Indirect specular light is baked to environment maps using cubemap entities, and the nearest cubemap in range is used to illuminate materials.

This guide uses a Cornell box-like scene with extra subdivisions on the faces to illustrate the effect of vertex lighting. The green and blue materials have specular reflections enabled.

SteamVR lighting preview fullbright.png
SteamVR lighting preview wireframe.png

Light Sources

All light sources can be set to provide direct or indirect light, or both. When per-pixel direct lighting is used, the the light entities can be manipulated at runtime to change the lighting.

SteamVR lighting preview light environment.png

light_environment

The light_environment entity provides outdoors environmental lighting, as well as options to add ambient occlusion and a base ambient term.

The base Color, Brightness and entity rotation settings control the directional sunlight, providing both direct and indirect lighting. The direct lighting can be switched between per-pixel real-time and baked, while the indirect lighting can only be baked. When real-time lighting is used, the sun light casts shadows using cascaded shadow maps, and the direct light and shadows are excluded from the baked lighting, leaving only the reflected indirect light to be baked.

The ambient sky is controlled with the setting in the Sky group. The Sky Color allows setting an uniform color, and the intensity controls its brightness. The Sky IBL Source option allows using the skybox texture set in the env_sky entity for image-based lighting, sampling the colors in the skybox for the ambient lighting instead. To use it, give the env_sky a name, and supply it in the Sky IBL property. It is recommended to use floating point HDR images for this.

The ambient occlusion controls allow extra shadows to be baked to areas with high occlusion, (indents, crevices, etc.), this can have limited use with vertex lighting unless the meshes have high detail.

The Ambient Light option allows adding a constant light to the entire level. This is unrealistic, and can produce bad results unless used with caution.

light omni

An omnidirectional point light source. Does not provide cascaded shadow maps. Many properties are carried over from the Source light entity.

All light
Indirect light

light_spot

A spot light source. Provides cascaded shadow maps as long as the light cone angle is set under 90 degrees. Many properties are carried over from the Source light_spot entity.

All light
Indirect light

Baking lighting

The VRAD2 tool used to bake lighting in SteamVR Home uses the photon mapping algorithm to bake lighting. This is a two-step process where the first step generates the photon map itself, a representation of the lighting made by ray tracing light packets from light sources and storing occurrences of packets hitting surfaces. This fist part is cached to file and can be re-used if the geometry or lighting hasn't been changed significantly between runs.

The second step generates the baked lighting onto meshes. Although some of the tools suggest that lightmapping support is in the works, the current version only supports vertex lighting, meaning that lighting is baked only to each vertex of the mesh. This can cause triangular artifacting and odd gradients in areas with high contrast lighting. Due to these effects it is not recommended to bake direct lighting on directional light sources.

Dynamic objects

The baking only directly affects static meshes. To enable dynamic objects such as physics props to be lit by baked lighting, two techniques are used.

SteamVR lighting indirect diffuse.png

For diffuse lighting, light probe volumes sample the ambient light inside them and generate voxel maps of the different ambient values, and the maps are stored as textures. This is similar to how ambient lighting is handled in the legacy Source engine, except there the volumes are defined by the automatically generated visleaf volumes, whereas in Source 2, the lack of sealed maps and a BSP portal visibility system requires us to map out the volumes ourselves.

Light probe volumes are defined with the env_light_probe_volume and env_combined_light_probe_volume entities, the latter also having an env_cubemap_box built-in.

There are two different ways light probe volumes can sample lighting, the default option samples the light directly from geometry around it by tracing (

Todo: Verify

). The other option is to indirectly sample the lighting using cubemaps (see below), enabled using the Calculate Diffuse Lighting Using Cubemap option in the light probe volume entity.

SteamVR lighting indirect specular.png

Ambient specular lighting is handled by environment maps (cubemaps). Cubemaps bake the environment around them into panoramic textures, which are then used for detailed reflections. Cubemaps only provide a fully accurate reflection at its point of origin, so it is recommended to place them at head height.

The left wall reflects the room using cubemap projection. Note the skewed objects in the room.

The default VR shader supports projecting the cubemap on its bounds and rendering the reflection from the correct position inside the room, using the Specular Cube Map Projection setting. This effect is enabled on a per-material basis, and can result in a correct mirror effect on the cubemap bounds. Anything inside the cubemap will be skewed though.


Direct lighting is handled per light source, and can be either per-pixel real time lit, or baked into the ambient lighting. A mixed option exists to enable real-time lighting on dynamic objects only, while retaining baked direct lighting on static objects.

Mixed direct lighting
Baked direct lighting. Note the real-time shadows on the rear balloonicorn.

Optimization

Baking time increases rapidly with increased map sizes, but there are several ways of combating this.

Photon mapping from environmental lights is performed from the edges of the map, defined by where the map geometry starts. If the map has far away background objects, photon mapping can take a very long time. To combat this, a light_importance_volume entity can be placed. This entity defines a volume to perform detailed photon mapping in. Outside it only simplified lighting is performed. Note that the importance volume is a single axis-aligned bounding box, so it can't be rotated, and multiple volumes are merged into a single one, making it less useful for maps that are not rectangularly shaped.

Text Cause
Photons 1/1 This is the first step of the photon map generation. This can be optimized by using a light_importance_volume.
LPV-Indirect/Direct The light probe volume voxel size generation. Can be optimized by decreasing the voxel resolution in light probe volume entities.
Tris 1/1 This is the second step of the photon map generation. The time here is dependent on the number of lighting samples, in this case the number of vertices in the static meshes of the map. The only way to optimize this is to simplify the models and meshes used in the map, or decrease the number of complex static objects.

Lightmapping

Upon release of the Half-Life Alyx preview environments, SteamVR Home was updated to include support for a lightmapping system similar to one used in the full game. Instead of baking lighting information onto the vertices of static mesh geometry, a lightmap texture can be generated with lighting detail not dependent on geometric complexity, but rather the resolution of the lightmap texture and user tunable biasing. For many lighting effects and scenes it is now possible to rely on accurate precalculations of lighting and shadows instead of the typical arrangement using expensive per-pixel runtime calculations for shadows and the direct component of a light and inferior baked vertex lighting for the indirect.

Usage

By default, a light entity placed in SteamVR Home will still have its directional component set to per-pixel (real-time). The take advantage of the lightmapper you will have to set it to 'Baked', though you will not see any change in the editor—all lights in the hammer viewports necessarily render as per-pixel lights with any indirect vertex lighting and cubemap reflections also being shown if they are present. If 'Generate Lightmaps' is enabled at build time, the direct component and shadows of any baked lights (along cubemap reflections, light probe volume textures and the indirect component for relevant lights) will be generated and then packaged into the finished map VPK. If this build setting is not enabled, previously generated vertex/preview lighting will be used instead.

The Hammer editor UI was adjusted and renames the original method to 'Preview Lighting'. Because it is much quicker to build, it is used during map development. Map makers can initiate (re)builds of vertex/preview lighting on the whole map (or a selection of objects) at some arbitrary point prior to build—whereas lightmaps will be built at the time of compilation, process everything, and take *much* longer to generate.

Warning.pngWarning:When shipping your environment, you will need to remove the _vrad3 folder in your addon’s game directory. These very hefty files will be too large for a workshop submission and the tools will crash when uploading. The map vpk already includes the final results of the lightmap process. Presumably, this folder is not deleted by the tools in the off chance it will be reused for entity-only builds, or perhaps other cases like advanced invocations of the vrad3 tool or distributed compilations.
Note.pngNote:The alternative option of pre-baking the direct component onto vertex data would generally not produce acceptable visuals and may also no longer function with the tools -- even if set to direct>baked, it will function as a per pixel light if lightmaps are not built. [TODO]


Toggling this flag in a light entity and setting the compile time toggle are the only necessary steps to use the new system, but it is also helpful to change the resolution of the lightmap for certain areas of your environment. Setting the lightmap bias of faces and static props will allow you to influence which areas of your map receive more (or less) detail. You may also enclose important areas of your map with meshes using the "tools/lightmap_playerspace" material—parts of your environment farther away from the faces of these meshes will have a lower lightmap bias.

Note.pngNote:It is easy for your hand-set lightmap bias values on faces to reset, either from heavy mesh work/merging, or just inadvertently within the editor. Try to keep well kempt selection sets and procedures for tracking and reapplying your preferred bias values.


In addition to (and exacerbated by) the long processing times, the primary difficulty presented when using the lightmapper is handling certain visual discrepancies between what is presented in the editor and what the lightmapper is capable of precalculating. You can view the final lightmap results without having to put on a VR HMD by setting a Hammer viewport to 'Game' mode, however, certain other things like the skybox and certain kinds of animated props might only render inside the headset or in the other Hammer views.

  • The most pertinent issue is the loss of the specular component when the light is fully baked. Half-Life Alyx supports 'Baked Light Indexing' which allows the specular component to render without the light being fully per-pixel, but SteamVR Home does not have this feature. You may opt for the specular component to be baked into the cubemaps, but achieving desired visual results may not always be feasible this way. (Disabling 'Render Specular' for all baked lights will have the editor view more closely resemble the game build—although the preview render of light_environment will not be affected by this entity flag.)
  • Next is the difference in shadow generation. First, the quality of shadows baked into the lightmap become dependent on overall resolution and careful biasing. Second, translucent materials will not block light like they do when the light is per pixel, so things like grass or chain link fence textures will not create interesting shadows in your environment. This might necessitate an alteration of the scene, use of a blocklight mesh, etc. (Hammer has baked lighting visualization modes to generate previews of what the lightmapper will be able to see and what shadows will eventuate in your environment. To achieve visual parity with a game build against the editor preview, you may also 'Disable Shadows' on objects that won't cast shadows when only hit by baked lights.)
  • Lastly, materials that feature the 'tree sway' parameter (allowing things like leaves and such to cheaply animate) can often not render properly in a lightmapped build. They might artifact, glitch, stretch etc. If you spot this, you will have to either set the prop to a 'prop_dynamic' or keep it as static and disable the 'Bake Lighting' flag. The latter option will use less resources and the end result on visuals is seemingly the same. Depending on the scene, this prop may then sometimes appear brighter than you would like, and lowering the prop's color value might help to appropriately darken it. (Take advantage of quick '512-Fast' builds to spot offending props early.)

Building

Running the lightmapper takes a long time, so finding ways to run quicker invocations during development will be important for your productivity and ability to iterate. The vrad3 process does not hang the editor, so you can work on another map or prefab, or even the map that is building (though the changes won't be present until the next build). Resolution and the Quality setting will be the main factor influencing build time. You should expect to build 4k 'Final' lightmaps for your shipping level, but can rely on more relaxed settings when iterating on your environment. Using the (limited) Tools Visualization Modes can help you spot problems with shadows, and preview lighting will still help you place cubemaps and adjust properties of the lights. Debugging luxel density and shadows is more difficult when testing with lower resolution lightmaps. It may help to section a large map off into prefabs such that your computer will be able to execute 4k (or even 8k) 'Fast' test builds of these separate sections which will result in grainy, but high resolution lighting detail that will help you find important areas where biasing should be adjusted.

Warning.pngWarning:You will likely not be able to build an 8k 'Final' lightmap without your computer having lots of RAM. 64GB is suggested, as the vrad3 process can use over 30 at certain stages. Furthermore, building 16K lightmaps is not supported in the SteamVR engine branch.
Icon-Bug.pngBug:The 'mat_luxels' ingame command does not seem to work in SteamVR, so you will not be able to debug luxel density with it.  [todo tested in?]
Tip.pngTip:You can copy/paste the resourcecompiler command line string shown at the bottom of the map builder dialog into a command prompt or batch file and build maps without having to run the workshop tools (thought you might still need to have Steam running). The compiled map VPK will need to be moved from the temporary location into your addon's game/maps directory before loading .

When using vertex lighting, cubemap textures are generated before the map is built. If generating lightmaps for your environment, you will need to initiate the process of generating the final cubemap textures that will be rendered in-game. You can do this with the 'Build cubemaps on load' toggle in the compile dialog and loading the map from there, or otherwise with the 'buildcubemaps' console command when the compiled map is loaded in tools mode. The textures will then be inserted into the map VPK.

Note.pngNote:If an object is close enough to multiple cubemaps it may be assigned to one different than what was seen with preview lighting. For these edge cases, you may have to assign the 'Lighting Origin' of an object to the desired cubemap volume.

Guidelines

A per-pixel light will create attractive and dynamic shadows (with configurable resolution) at runtime and employ a specular component, but they are very expensive to render and so it is recommended to use baked lights whenever possible to claim higher VR performance. Deciding which lights will use a per-pixel direct component and their placement will be important considerations. Keep them far enough away from other per-pixel lights and shining directly on as few surfaces as you need then to. Meanwhile, a fully baked light is more or less able to accomplish what it is able to for free. Beyond their performance cost, it is generally hard to have many per-pixel lights in one environment without the engine running into problems.

Note.pngNote:Attempting to keep a light per-pixel (so that the specular component can be retained), but eschewing the performance hit of dynamic shadows by setting 'Cast Shadows' to 'Baked Only' does not seem to work. If the direct component of the light is per pixel, then the shadows will be as well.


If your level is an open outdoor area, shadows from translucent foliage might be of particular importance to the look of your scene, and it would be acceptable to have the one per-pixel directional environment light doing most of the work to illuminate and shadow your environment. Perhaps your level is a circular atrium in a chromed space station. Having one hefty spot light filling up that large scene can be set to per pixel so all the metal materials (which otherwise look considerably dull and crude without the specular component) can shine properly. Should your level be something more complicated, sprawling, and/or indoors, you will have to be more diligent about how you use your resources, and which materials can look decent without specular and which showcase areas need the full treatment of a per-pixel light.

In conclusion, the SteamVR tools received just enough of an update to make the Alyx sample environments have visual parity with the full game. You will have to work within some new limitations and apply some technical and artistic creativity but the performance benefits, workflow improvements, and increase in indirect lighting detail are worth it. Good luck!

See also