Lighting System Specifications

Introduction

The static lightmap generation system has been redesigned and reengineered from the ground up to accommodate the growing need for better lighting in Ritual’s games. These changes have required code changes to the map compiling utility (q3map.exe), the map editor (q3radiant.exe), and the core game engine itself. This document is meant to serve three purposes: first, to extend the TDD (Technical Design Doc) as it relates to the specific features of the lighting system; second, as a tutorial for level designers and lighting specialists; third, as a canonical reference resource for future lighters, engineers, and programmers alike.

General Principles

In designing each aspect of the new lighting system, input was gathered from a number of contributors both inside Ritual and out. The resulting design was a consolidation of all of these inputs and, for the most part, accommodates the desires of each (at least in spirit if not in specific implementation details). Efforts have been made to create the system in such a way that it is flexible enough to encompass different methods and techniques into a generalized system that can service the needs of everyone involved.

The most obvious philosophical change in lighting paradigms is that map lighters now have a much greater amount of control over various aspects of the light. A light’s brightness is now independent of its falloff geometry, which in turn is broken up into several individually controllable aspects. Each of these aspects will be covered in detail throughout the course of this document.

Another important philosophical design decision of the new lighting system can be summarized by the phrase: “realism is good; total control is better”. Lighting engineers now have the ability to (reasonably) reproduce realistic behavior in their lights; however, they also have the ability to explicitly change any aspect of a light such that it will behave in a less realistic but more desirable fashion.

The Lighting Process

The following is a step-by-step process of how lighting works in the TikiEngine.

Step 1: Placement of light entities in Radiant
The lighting engineer uses the map editor to create, copy, and modify light sources in a map. Lights are previewed visually in the editor, both in the orthogonal (2d) and perspective (3d) views. OpenGL is used to render spotlights as cones, point-lights as spheres, and so on. The lighting engineer tweaks each of the lighting keys (discussed later) until (s)he gets the desired light shape and properties. The map is then saved.

Step 2: Compilation of static lightmap data using q3map
The lighting engineer then generates the static lightmap data for the map by running the map compiler on the level, using the command:

Or by running each stage of q3map as a separate task:

At this stage, the q3map tool calculates the lightmap data for the entire level by applying the following steps:

  1. For each surface in the world, a sample point is generated for every 16 world units, forming a coarse grid overlaying the entire surface area of the world.

  2. For each sample point, every light source in the map is checked to see if it contributes any light to that sample point. If the sample point is outside a point-light’s (or spotlight’s) maximum radius (called “falloff_end_dist”), that light is not considered for that sample point.

  3. For point-lights, the amount of light added to the lightmap at each sample point is calculated using the following steps:

    • a. If the sample point is inside the inner radius of the light (called “falloff_start_dist”), the light is applied to the sample point at the light’s maximum brightness.

    • b. If the sample point is outside the inner radius of the light, the amount of light is somewhere between 0 and maximum brightness; the exact value depends on the distance the sample point is away from the inner radius as well as the nature of the falloff curvature (“falloff_curvature”), which can be anywhere from a straight-linear drop-off to a steep, inverse-squared drop-off.

  4. For spotlights, the amount of light added to the lightmap at each sample point is calculated using the following steps:

    • a. If the sample point is inside the area of the inner cone (“angle_hotspot”), it is lit normally as if the light were a normal point light.

    • b. If the sample point is outside the inner cone but still within the outer cone (“angle_penumbra”), it is partially lit in proportion to its closeness to the inner cone.

    • c. If the sample point lies outside the outer cone, it is not lit by the spotlight at all (unless the “spherical_ambient” key is nonzero for that light).

  5. For sunlight, the amount of light added to the lightmap at each sample point is calculated using the following steps:

    • a. A ray is traced from the sample point backward along the direction of the sunlight vector. If the ray collides with a sky brush or sky portal before any other solid object, that sample point is fully lit by the sunlight source.

Step 3: The map is rendered by the engine using textures and lightmap data.
When drawing a surface of the world, the engine generally makes (at least) two rendering passes. The first pass is the opaque application of the surface’s texture. The second pass is the application of the lightmap data that was precalculated during the q3map –light stage. The lighting pass is done using the lightmap’s RGB value as a color filter against pixels in the texture at each pixel on the surface; each of the lightmap’s RGB components is multiplied by the pixel’s matching component to produce the net resulting color component. Thus, a lightmap of (1.0, 0.5, 0.0) on a medium-grey texel (0.6, 0.6, 0.6) will produce a resulting pixel value of (0.6, 0.3, 0.0).

Light Source Types

There are several different types of light sources available to lighting engineers. All types are represented by a single “light” entity; however, some types require additional objects in order to be properly defined:

Type 1: Point Lights
A point light is the most common type of light. It is the simplest to define, the easiest to visualize in the editor, and the fastest to calculate during the q3map –light phase of map precompilation.

Point lights emit light outward spherically from their origin, affecting all surfaces within their outermost radius (or “falloff_end_dist”, discussed below). They cast static shadows onto the lightmap when a line of sight ray between the point light source and the surface sample point is obstructed by another object.

Type 2: Spotlights
A spotlight is the next most common type of light. It bears all the properties of a point light; however, a spotlight confines its area of affect into a cone along the direction of the spotlight. This direction is determined by associating the light with a target – usually an info_null – that has a “targetname” key matching the light’s own “target” key. In order to turn a point light into a spotlight, simply associate it to a target entity in this manner.

Type 3: Sunlights
Sunlights are essentially point lights that are infinitely far away. They are constructed in a manner similar to a spotlight (i.e. using an info_null as a target entity). To make a spotlight into a sunlight, simply check the box in the Light Properties dialog labeled “Directional (parallel) light source”.

Once a light becomes a sunlight, its actual position becomes completely meaningless. The light now simply serves as a prototype for defining a light source infinitely far away (such as the sun or moon). All sky brushes and sky portals in the level will now act as parallel emitters for this sunlight; in other words, light of the type specified will be cast in parallel lines along the vector specified by the light’s target entity from every point on the surface of every sky brush or sky portal.

Lighting Keys

In the map editor, a light source is represented as an entity. Each entity has a number of key-value pairs (or epairs) which store the various properties of that light. The following is a description of each of the newly-added epair keys:

“brightness”
Called Overall Brightness in the Light Properties dialog, this determines the maximum brightness multiplier for the light at its brightest point (i.e. its origin). If “brightness” is 1.0, the light makes any surface inside its inner radius appear fullbright (assuming the light is pure white). If “brightness” is 0.5, the light’s brightest spot is only 50% as bright as a normal light at any given point. Note, however, that its geometry – including inner and outer radius, falloff curvature, etc. – will be completely unchanged.

Note also that if “brightness” is negative, the light is subtractive rather than additive; i.e. it actually removes light from surrounding surfaces rather than adding light to them.

The “brightness” key may hold any value between –1.0 and 1.0.

“dot_product_weight”
Called Dot Product Effect in the Light Properties dialog, this determines the degree to which the dot product (angle of incidence) will affect a light’s effect on a surface. When “dot_product_weight” is 0.0, the angle of the surface being lit is not taken into account at all (other than the fact that surfaces facing completely away from the light are never lit). When “dot_product_weight” is 1.0, the amount of light received on a given surface is proportional to how perpendicular / straight-on that surface is to the source of the light. A value of 0.0 produces unrealistic (but easy-to-control) quake-like lighting; a value of 1.0 produces realistic lighting with more complex subtleties. In general, a value somewhere in-between these two extremes is desirable. The default value is 0.5.

“_color”
The color of the light emitted by a light source. This key has a 3-float vector for its value, such as “1.0 0.5 0.0” (an orange color). The “_color” key is set by choosing a color in the entity color-picker (default “K” in Radiant), although it may also be edited by hand. Clicking on the color sample in the Light Properties dialog also opens the color picker for all currently selected objects.

Note that lights may never have a color that is not component-saturated; in other words, all light colors must have at least one component (either Red, Green, or Blue) that is set to 1.0. Non-saturated colors are automatically saturated by Radiant (if set through the color picker) and by q3map at load-time.

“falloff_curvature”
Called Falloff Curvature in the Light Properties dialog, this determines the steepness of the falloff in intensity as it decreases from maximum at the inner radius (“falloff_start_dist”) to the zero at the outer radius (“falloff_end_dist”). A “falloff_curvature” of 0.0 represents a gentle linear falloff; a value of 1.0 represents a steep, rapidly-decreasing (inverse-squared) falloff. As with “dot_product_weight”, “falloff_curvature” is easiest to control (and least realistic) at 0.0 and is most realistic (but more subtly complex to control) at 1.0. The default value is 0.5.

“falloff_start_dist”
Called Falloff Start Distance in the Light Properties dialog. This is the inner radius; that is, the distance – in world units – away from the light source within which any surface is fully lit by the light. Note that although this means “fullbright” in some cases, other factors are considered as well: the color of the light, the dot product of the light’s angle hitting the surface (if enabled), and so on. The “falloff_start_dist” value of a currently selected light is represented graphically in both 2d and 3d views in Radiant as translucent spheres of the (normalized) color of the light. (Note: make sure “Show actual colors in XY” is checked ON in Edit->Preferences.)

For spotlights, this represents the height of the inner cone, as is consistent with spherical lights (considering the cone as a segment of a sphere).

The hotkeys to quickly adjust “falloff_start_dist” for all selected lights is (by default) : ALT + CTRL + [ to decrease, ALT + CTRL + ] to increase.

“falloff_end_dist”
Called Falloff End Distance in the Light Properties dialog. This is the outer radius; that is, the distance – in world units – away from the light source outside of which any surface is totally unaffected by the light. The “falloff_end_dist” value of a currently selected light is represented graphically in both 2d and 3d views in Radiant as translucent spheres of the (normalized) color of the light. (Note: make sure “Show actual colors in XY” is checked ON in Edit->Preferences.)

For spotlights, this represents the height of the outer cone, as is consistent with spherical lights (considering the cone as a segment of a sphere).

The hotkeys to quickly adjust “falloff_end_dist” for all selected lights is (by default) : ALT + SHIFT + [ to decrease, ALT + SHIFT + ] to increase.

“angle_hotspot” (spotlights only)
Called Hotspot Angle in the Light Properties dialog, this field is used only by spotlights. The “angle_hotspot” value of the light determines the total angle, in degrees, of the inner cone of the spotlight. Any point within this inner cone is lit as though the light source were a normal spherical point light. Likewise, any point within this inner cone AND within the inner radius (as determined by “falloff_start_dist”) is fully lit by the light.

The hotkeys to quickly adjust “angle_hotspot” for all selected lights is (by default) : ALT + CTRL + < to decrease, ALT + CTRL + > to increase.

“angle_penumbra” (spotlights only)
Called Penumbra Angle in the Light Properties dialog, this field is used only by spotlights. The “angle_penumbra” value of the light determines the total angle, in degrees, of the outer cone of the spotlight. Any point outside this outer cone is completely unlit / unaffected by the spotlight (unless “spherical_ambient” is nonzero – see below). Points that fall outside the inner cone (“angle_hotspot”) but inside the outer cone (“angle_penumbra”) are partially lit by the spotlight, with a lateral linear falloff from normal brightness (at the edge of the inner cone) to total darkness (at the edge of the outer cone).

The hotkeys to quickly adjust “angle_penumbra” for all selected lights is (by default) : ALT + SHIFT + < to decrease, ALT + SHIFT + > to increase.

“spherical_ambient” (spotlights only)
Called Spherical Ambient in the Light Properties dialog, this field is used only by spotlights. The “spherical_ambient” value of the light determines the fractional amount of the spotlight’s intensity that is conveyed outside the outer (“angle_penumbra”) cone. A value of 0.0 (default) indicates that the spotlight emits no light outside its outer cone. A value of 0.5 indicates that surfaces outside the spotlight’s cone are lit as if by a normal, spherical point light of half the intensity of the spotlight (but with the same basic geometry, in terms of “falloff_start_dist”, “falloff_end_dist”, and “falloff_curvature”). A value of 1.0 (maximum) makes the spotlight behave exactly like a normal spherical point light, since points outside the cone are lit with an equal amount of intensity as the points inside the cone.

Miscellaneous Notes Regarding Lighting Keys

  • Sunlights ignore the following keys: falloff_curvature, falloff_start_dist, falloff_end_dist, hotspot_angle, hotspot_penumbra, spherical_ambient

  • Point lights ignore the following keys: hotspot_angle, hotspot_penumbra, spherical_ambient

  • falloff_start_dist can never be greater than falloff_end_dist; similarly, falloff_end_dist can never be less than falloff_start_dist. (Note that the distinction between these two statements refers to two different types of clamps, depending on which value is being changed to exceed the other.) The Light Properties dialog will automatically stretch the other field to accommodate the one being moved in excess of these limits.

  • Likewise, angle_hotspot can never be greater than angle_penumbra, and vice-versa. Also enforced by the Light Properties dialog.

  • The keys light, style, falloff, minlight, radius, angles, spot_angle, spot_dir, and other legacy quake-style lighting epair keys are now obsolete and are completely ignored by the compiler.

  • Sunlights automatically enforce a dot_product_weight of 1.0.