Second Life Lighting System - ApertureViewer/Aperture-Viewer GitHub Wiki

Analysis of the Second Life Lighting System (via Aperture Viewer Codebase)

This document provides a technical analysis of the core lighting system used in Second Life, based on a review of the Aperture Viewer v1.0.0 codebase. It is understood that, at the time of this analysis, the fundamental deferred shading pipeline, light source handling, and shadow mapping logic within the reviewed Aperture code largely reflect the implementation inherited from its upstream source (Firestorm Viewer, derived from the official Second Life Viewer). While Aperture may have specific shader modifications (e.g., potentially related to SSAO integration or PostFX), the core lighting and shadow rendering architecture described here is believed to be representative of the standard system used across current viewers, not a lighting system uniquely implemented by Aperture Viewer itself.

(Analysis based on review of relevant C++ files like pipeline.cpp, llprimitive.cpp, llvovolume.cpp, llgl.cpp, shader files, etc., from the Aperture v1.0.0 source.)

1. Overall Architecture: Deferred Shading

The core of the lighting system relies on a Deferred Shading pipeline. This approach separates geometry rendering from lighting calculations for efficiency, especially with many light sources.

  • G-Buffer Generation (renderGeomDeferred): Opaque and alpha-masked objects are rendered first. Their surface properties (Albedo/BaseColor, Normal, Specular/ORM, Emissive, Material Flags) are written into multiple render targets (mRT->deferredScreen). Shaders responsible include diffuseF.glsl, pbropaqueF.glsl, materialF.glsl, avatarF.glsl, etc.
  • Lighting Calculation (renderDeferredLighting): This pass reads the G-Buffer data for each pixel. It calculates and accumulates the lighting contribution from global sources (Sun/Moon) and selected local lights (Point/Spot) into a lighting buffer (mRT->screen, possibly via mRT->deferredLight). It integrates Ambient Occlusion (SSAO) and atmospheric effects. Key shaders: sunLightF.glsl, pointLightF.glsl, multiPointLightF.glsl, spotLightF.glsl, multiSpotLightF.glsl, softenLightF.glsl.
  • Shadow Mapping (generateSunShadow, renderShadow): Runs separately to generate depth maps from the perspective of shadow-casting lights (Sun/Moon cascades and up to two Spotlights) into mRT->shadow[] and mSpotShadow[]. Uses simplified shaders (shadowF.glsl, etc.).
  • Forward Rendering (renderGeomPostDeferred): Handles objects unsuitable for deferred shading (alpha-blended transparency, water, UI, debug visuals). These are rendered after the deferred lighting pass and typically perform their own lighting calculations (sampling environment probes and potentially a limited set of dynamic lights passed via uniforms).
  • Post-Processing (renderFinalize): Applies final screen-space effects like Glow, Depth of Field, Tonemapping, Anti-Aliasing, Sharpening, etc., to the composed image.

2. Light Source Types & Representation

  • Sun/Moon: A single primary directional light. Its color and direction are global, managed by LLEnvironment / LLSettingsSky and passed as uniforms (e.g., sun_dir, sunlight_color). Its contribution is significantly affected by atmospherics.
  • Ambient Light: A non-directional base illumination level (ambient_color), also from environment settings.
  • Local Point Lights: Omni-directional lights originating from a point. Represented by LLVOVolume objects where light parameters are enabled. Key properties (radius, color, falloff) are stored in parameter blocks (LLLightParams defined in llprimitive.h).
  • Local Spot Lights: Directional lights emitting from a point within a cone. Also represented by LLVOVolume. Distinguished from point lights via LLVOVolume::isLightSpotlight(). Additional parameters (direction derived from object rotation, cutoff angle, focus, projective texture ID) are stored in parameter blocks (LLLightImageParams defined in llprimitive.h).
  • Emissive Surfaces: Objects whose materials inherently glow. Handled via the emissive channel in the G-Buffer, written by shaders like pbropaqueF.glsl.

3. Local Light Management: Selection & Limits

  • Parameter Storage: All local light properties are stored within the LLVOVolume object using the parameter block system defined in llprimitive.h/.cpp. This system manages different data types associated with objects (e.g., PARAMS_LIGHT, PARAMS_LIGHT_IMAGE). Adding new light parameters (like for Square Lights) involves modifying these structures and their pack/unpack logic.
  • Initial Candidate Selection (pipeline.cpp::calcNearbyLights):
    • Identifies potential lights from the global list (mLights).
    • Filters based on distance to camera relative to light radius (calc_light_dist), intensity (getLightIntensity), selection status (isSelected), whether the light is moving (LLDrawable::ACTIVE state), and the sRenderAttachedLights setting.
    • Adds qualifying lights to a sorted set (mNearbyLights).
  • Processing Limit: The number of lights from mNearbyLights actually processed for rendering illumination is limited by the RenderLocalLightCount setting. Lights exceeding this count (farthest first) are faded out using Light::fade. Based on the provided code, no other factors (like GPU load or scene complexity) dynamically adjust this limit or further cull lights from this list before rendering.
  • Shadow Limit: There is a hardcoded limit of TWO spotlights that can cast dynamic shadows simultaneously. This is evident from the array sizes (mSpotShadow[2], mTargetShadowSpotLight[2]) and iteration limits (loops 0 to 1) in generateSunShadow and related logic.

4. Light Rendering Process

  • Deferred Point Lights:
    • Rendered by drawing bounding volumes (cubes).
    • Shaders (pointLightF.glsl, multiPointLightF.glsl) read G-Buffer data (position, normal, material props).
    • Calculate attenuation based on distance, radius, and falloff uniforms.
    • Compute lighting using appropriate model (legacy Blinn-Phong or PBR).
    • Result is additively blended.
  • Deferred Spot Lights:
    • Similar process to point lights but confined to a cone.
    • Use a projection matrix (proj_mat) and sample a projective texture (projectionMap) for shape and color modulation.
    • Apply angular attenuation based on spot direction and cutoff.
    • Sample one of the two spot shadow maps (shadowMap4/shadowMap5) if designated (proj_shadow_idx).
  • Forward-Lit Transparency: Objects in the renderGeomPostDeferred pass typically use simpler lighting models within their fragment shaders, sampling environment probes and potentially a few lights passed as standard uniforms (likely sourced from LLLightState/setupHWLights).

5. Shadow System

  • Sun/Moon Shadows: Uses Cascaded Shadow Maps (CSM). generateSunShadow splits the view frustum based on mSunClipPlanes (up to 4 cascades) and renders depth from the light's view into mRT->shadow[]. sunLightF.glsl samples the appropriate cascade using sampleDirectionalShadow and pcfShadow based on pixel depth.
  • Spotlight Shadows: Limited to two casters. setupSpotLight selects the two highest-priority spotlights (based on updateSpotLightPriority which uses screen size) and assigns them shadow map slots (mSpotShadow[0..1]). spotLightF.glsl/multiSpotLightF.glsl sample these using sampleSpotShadow and pcfSpotShadow. A fading mechanism (mSpotLightFade) smooths transitions between shadow casters.
  • Technique: Percentage-Closer Filtering (PCF) is used for soft shadow edges. Depth bias (shadow_bias, spot_shadow_bias) and slope-scale bias (implied by offset logic, e.g., shadow_offset) are applied to reduce self-shadowing artifacts.

6. PBR Integration

  • PBR materials (GBUFFER_FLAG_HAS_PBR) have a distinct path in lighting shaders.
  • Utilizes data from ORM texture (Occlusion, Roughness, Metallic) sampled from specularMap.
  • Relies heavily on Image-Based Lighting from Reflection Probes (reflectionProbes, irradianceProbes) sampled via sampleReflectionProbes and processed with functions like pbrIbl.
  • Direct lighting uses PBR functions like pbrPunctual which incorporate roughness, metallic, and Fresnel terms.

7. Key Code References

  • Main Pipeline Logic: pipeline.cpp, pipeline.h
  • Light Object Parameters: llprimitive.h, llprimitive.cpp, llvovolume.h, llvovolume.cpp
  • Low-Level GL State: llgl.h, llgl.cpp (defines LLLightState)
  • Shader Management: llshadermgr.h, llshadermgr.cpp, llviewershadermgr.h, llviewershadermgr.cpp
  • Lighting Shaders: deferred/sunLightF.glsl, deferred/pointLightF.glsl, deferred/multiPointLightF.glsl, deferred/spotLightF.glsl, deferred/multiSpotLightF.glsl, deferred/softenLightF.glsl, deferred/deferredUtil.glsl
  • Shadow Shaders: deferred/shadowF.glsl, deferred/shadowUtil.glsl

8. Summary of Capabilities and Confirmed Limitations

  • Capabilities:
    • Robust deferred shading pipeline.
    • Handles numerous local lights simultaneously, limited primarily by performance (RenderLocalLightCount) rather than fixed slots.
    • Full PBR material support integrated with IBL (Reflection Probes).
    • Cascaded Shadow Maps for Sun/Moon.
    • PCF filtering for soft shadows.
    • Sophisticated atmospheric effects.
  • Confirmed Limitations (Based only on provided code):
    • Strict limit of TWO dynamic shadow-casting spotlights.
    • No built-in support for Area Light types (only Point and Spot sources are modeled).
    • Local light selection criteria are confined to distance, radius, intensity, selection status, active state, attachment status, and the count limit, with screen area used only for prioritizing the two spotlight shadow slots.

This analysis is based solely on the specific code files reviewed from the Aperture Viewer project and represents the system's implementation therein.