Search Unity

Light Probe Proxy Volume: 5.4 Feature Showcase

February 3, 2016 in Technology | 5 min. read
Topics covered

Is this article helpful for you?

Thank you for your feedback!

Unity 5.4 has entered beta and a stand out feature is the Light Probe Proxy Volume (LPPV). I just wanted to share with you all what it is, the workflow and some small experiments to show it in action.

Correct as of 30.01.2016 – Subject to changes during 5.4 beta.

What Is A Light Probe Proxy Volume?

The LPPV is a component which allows for more light information to be used on larger dynamic objects that cannot use baked lightmaps, think Skinned Meshes or Particle Systems. Yes! Particle Systems receiving Baked Light information, awesome!

How To Use The LPPV Component?

The LPPV component requires an exisiting Light Probe Group. The component is located under Component -> Rendering -> Light Probe Proxy Volume, by default, the component looks like this:

Light Probe Proxy Volume Component_1

It’s a component you will need to add to the GameObject such as a 3d model or even a Light Probe Group. The GameObject you want to be affected by the LPPV needs to have a MeshRenderer / Renderer that has the Light Probes property set to “Use Proxy Volume":

Light Probe Proxy Volume Component_3

You can borrow an existing LPPV component which is used by another GameObject by using the Proxy Volume Override, just drag and drop it into the property field for each Renderer you want to use it on. An example: If you added the LPPV component to the Light Probe Group object, you can then share that across all renderers with the Proxy Volume Override property:

Use Proxy Volume

Setting up the Bounding Box:

There are three options for setting up your Bounding Box:

  • Automatic Local
  • Automatic World
  • Custom

Automatic Local:

Default property setting – the bounding box is computed in local space, interpolated light probe positions will be generated inside this box. 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, same behaviour for Automatic World.

Light Probe Proxy Volume Component_1

Automatic World:

A world-aligned bounding box is computed. Automatic Global and Automatic Local options should be used in conjunction with Proxy Volume Override property on other Renderers. Additionally you could have a hierarchy of GameObjects that use the same LPPV component set on a parent in the hierarchy.

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


Empowers you to edit the bounding box volume yourself in the UI, changing the size and origin values in the Inspector or by using the tools to edit in the scene view. Bounding box is specified in local space of the GameObject. You will need to ensure that all the renderers are within the bounding box of the LPPV in this case.

Light Probe Proxy Volume Component

Setting Up Resolution / Density:

After setting up your bounding box, you need to then consider the density / resolution of the Proxy Volume. To do this there’s two options available under Resolution Mode:


Default property setting – set a value for the density i.e. number of probes per unit. Number of probes per unit is calculated in the X, Y and Z axis, so defined by the bounding box size.


Set up custom resolution values in the X, Y and Z axis using the drop down menu. Values start at 1 and increment to a power of 2 up to 32. You can have 32x32x32 interpolating probes

Interpolating Probes

Performance Measurements To Consider When Using LPPV:

Keep in mind the interpolation for every batch of 64 interpolated light probes will cost around 0.15ms on CPU (i7 – 4Ghz) (at the time of Profiling). The light probe interpolation is multi-threaded, anything less than or equal to 64 interpolation light probes will not be multi-threaded and will run on the main thread.

Using Unity’s built-in Profiler you can see BlendLightProbesJob on the main thread using the Timeline viewer, if you increase the amount of interpolated light probes to more than 64 you will see BlendLightProbesJob on the worker thread as well:


The behaviour for just one batch of 64 interpolated light probes is it will run only on the main thread and if there are more batches (>64) it will schedule one on the main thread and others on the worker threads, but this behaviour is just for one LPPV. If you have a lot of LPPVs with less than 64 interpolated light probes each, they will all run on the main thread.

Hardware Requirements:

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

Sample shader for particle systems that uses ShadeSHPerPixel function:

The Standard shaders have support for this feature. If you want to add this to a custom shader, use ShadeSHPerPixel function. Check out this sample to see how to use this function:

Shader "Particles/AdditiveLPPV" {

_MainTex ("Particle Texture", 2D) = "white" {}
_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)

Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Blend SrcAlpha One
ColorMask RGB
Cull Off Lighting Off ZWrite Off

#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;
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);
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);
> += ambient;
UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode
return col;
February 3, 2016 in Technology | 5 min. read

Is this article helpful for you?

Thank you for your feedback!

Topics covered