Version: 5.4 beta (switch to 5.3)
Light Probes
Lightmap Parameters

Light Probe Proxy Volume

Switch to Scripting

The Light Probe Proxy Volume component allows you to use more lighting information for large dynamic objects that cannot use baked lightmaps (for example, large Particle Systems or Skinned Meshes).

By default, a probe-lit Renderer receives lighting from a single light probe that is interpolated between the surrounding light probes in the scene. Because of this, objects have constant ambient lighting across the surface. This lighting has a rotational gradient because it is using spherical harmonics, but it lacks a spatial gradient. This is more noticeable on larger objects or Particle Systems. The lighting across the object matches the lighting at the anchor point, and if the object straddles a lighting gradient, parts of the object may look incorrect.

The Light Probe Proxy Volume component generates a 3D grid of interpolated light probes inside a bounding volume. You can specify the resolution of this grid in the UI of the component. The spherical harmonics (SH) coefficients of the interpolated light probes are uploaded into 3D textures. The 3D textures containing SH coefficients are then sampled at render time to compute the contribution to the diffuse ambient lighting. This adds a spatial gradient to probe-lit objects.

The Standard Shader supports this feature. If you want to add this to a custom shader, use the ShadeSHPerPixel function. See the code example at the end of this document to learn how to implement this function.

Hardware requirements

The component requires at least Shader Model 4 graphics hardware and API support, including support for 3D textures with 32-bit floating-point format and linear filtering.

To work correctly, the scene needs to contain light probes via Light Probe Group components. If a requirement is not fulfilled, the Renderer or Light Probe Proxy Volume component inspector displays a warning message.

Light Probe Proxy Component

When to use the component

Most of the Renderer components in Unity contain a property called Light Probes.

There are three options for this property:

  • Off - the Renderer doesn’t use any interpolated light probes.

  • Blend Probes (default value) - the Renderer uses one interpolated light probe.

  • Use Proxy Volume - the Renderer uses a 3D grid of interpolated light probes.

When you set the Light Probes property to Use Proxy Volume, the GameObject must have a Light Probe Proxy Volume (LPPV) component attached. You can add a LPPV component on the same GameObject, or you can use (borrow) a LPPV component from another GameObject using the Proxy Volume Override property. If Unity cannot find a LPPV component in the current GameObject or in the Proxy Volume Override GameObject, a warning message is displayed at the bottom of the Renderer.

An example of a simple Mesh Renderer using a Light Probe Proxy Volume component
An example of a simple Mesh Renderer using a Light Probe Proxy Volume component

In the scene above, there are two planes on the floor using materials that emit a lot of light. Note that:

  • The ambient light changes across the geometry when using a LPPV component. If you use one interpolated light probe creates a constant color on each side of the geometry.

  • The geometry doesn’t use static lightmaps, and the spheres don’t represent light probes but interpolated light probes. They are part of the Gizmo Renderer.

How to use the component

By default, the LPPV component looks like this:

The area in which the 3D grid of interpolated light probes are generated is affected by the Bounding Box Mode property.

There are three options:

  • Automatic Local (default value) - a local-space bounding box is computed. The interpolated light probe positions are generated inside this bounding box. If a Renderer component isn’t attached to the GameObject, then a default bounding box is generated. The bounding box computation encloses the current Renderer and all the Renderers down the hierarchy that have the Light Probes property set to Use Proxy Volume.

  • Automatic Global - a bounding box is computed which encloses the current Renderer and all the Renderers down the hierarchy that have the Light Probes property set to Use Proxy Volume. The bounding box is world-aligned.

  • Custom - a custom bounding box is used. The bounding box is specified in the local-space of the GameObject. The bounding box editing tools are available. You can edit the bounding volume manually by modifying the Size and Origin values in the UI.

The difference between Automatic Local and Automatic Global is that in Automatic Global mode, the bounding box is more expensive to compute when a large hierarchy of GameObjects uses the same LPPV component from a parent GameObject - but the resulting bounding box may be smaller in size, meaning the lighting data is more compact.

The number of interpolated light probes from within the bounding volume is affected by the Proxy Volume Resolution property. There are two options:

  • Automatic (default value) - the resolution on each axis is computed using the number of interpolated light probes per unit area that you specify, and the size of the bounding box.

  • Custom - you can specify a different resolution on each axis (see below).

Note: The final resolution on each axis must be a power of two, and the maximum value of the resolution is 32.

Probe Position Mode specifies the relative position of an interpolated light probe to a cell center. This option may be useful in situations when some of the interpolated light probes pass through walls or other geometries and cause light leaking. The example below shows the difference between Cell Corner and Cell Center in a 2D view, using a 4x4 grid resolution:

Images for comparison

  1. Simple Mesh Renderer using Standard Shader

    With Light Probe Proxy Volume (resolution: 4x1x1)
    With Light Probe Proxy Volume (resolution: 4x1x1)
    Without Light Probe Proxy Volume
    Without Light Probe Proxy Volume
  2. Skinned Mesh Renderer using Standard Shader

    With Light Probe Proxy Volume (resolution: 2x2x2)
    With Light Probe Proxy Volume (resolution: 2x2x2)
    Without Light Probe Proxy Volume
    Without Light Probe Proxy Volume

Sample shader for particle systems that uses ShadeSHPerPixel function:

Shader "Particles/AdditiveLPPV" {
Properties {
    _MainTex ("Particle Texture", 2D) = "white" {}
    _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
}

Category {
    Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    Blend SrcAlpha One
    ColorMask RGB
    Cull Off Lighting Off ZWrite Off
    
    SubShader {
        Pass {
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_particles
            #pragma multi_compile_fog

            // Don't forget to specify the target
            #pragma target 3.0
            
            #include "UnityCG.cginc"
            
            // You have to include this header to have access to ShadeSHPerPixel
            #include "UnityStandardUtils.cginc"
                        
            fixed4 _TintColor;
            sampler2D _MainTex;
            
            struct appdata_t {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;                
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float3 worldPos : TEXCOORD2;
                float3 worldNormal : TEXCOORD3;
            };
            
            float4 _MainTex_ST;

            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.color = v.color;
                o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {           
                half3 currentAmbient = half3(0, 0, 0);
                half3 ambient = ShadeSHPerPixel(i.worldNormal, currentAmbient, i.worldPos);
                fixed4 col = _TintColor * i.color * tex2D(_MainTex, i.texcoord);
                col.xyz += ambient;
                UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode
                return col;
            }
            ENDCG 
        }
    }   
}
}

Light Probes
Lightmap Parameters