Issues tryint to add Light Probe support to instanced shaders

Hello. Here’s the problem.

I have a bare-bones shader with instancing enabled. Here it is:

Shader "Unlit/USH_InstancedLightProbesTest"
{
    Properties
    {

    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 300

        Pass
        {
            Name "FORWARD"
            Tags { "LightMode" = "ForwardBase" }

            Blend One Zero
            ZWrite On

            CGPROGRAM
            #pragma target 3.0

            #pragma multi_compile_fwdbase
            #pragma multi_compile_instancing

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                UNITY_VERTEX_INPUT_INSTANCE_ID
                float4 vertex : POSITION;
            };

            struct v2f
            {
                UNITY_VERTEX_INPUT_INSTANCE_ID
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appdata v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);
                return 1;
            }
            ENDCG
        }
    }
}

The problem here is that this shader doesn’t work with instancing if light probes are enabled (it’s refuses to instance at all, the Frame Debugger states that “The objects are affected by different light probes”. If I force the mesh renderers not to use light probes then the instancing works. Instancing will also work if I will have “Always” as a “LightMode” tag value (or remove the tag entirely). UnityCG.cginc is unmodified.

Due to no reasonable documentation for adding instancing with light probes and/or lightmaps support and having no luck finding the answer in the standard shader and generated surface shader, I ask here: how can I do this and where can I find direct examples of the implementation? Thank you.

It’s covered a bit here:

The TLDR is you need to use a Light Probe Proxy Volume, aka a LPPV.

Or you need to manually calculate and pass the light probe data to shaders as the built in probe data isn’t setup to be instanced. But converting the probe data to what Unity’s shaders use isn’t exposed anywhere in Unity’s code, so you have to search online for how to calculate that data from the interpolated probe data. And Unity breaks instancing even with a LPPV, so even then it doesn’t work properly even through it should. :frowning:

But wait, that doesn’t add up at all… Standard shader and surface shaders can all use instancing with light probes, they don’t need LPPV at all. I dug in them and the generated surface shader code and found no signs of them manually calculating/interpolating the SH data. I do not draw my meshes using C# apis, they are drawn using Mesh Renderers, so the SH data needs to be calculated nicely by Unity itself if the standard and surface shaders can work with them.

The manual even states this: “You can use GPU Instancing to automatically batch dynamic Mesh Renderers affected by baked Light Probes (including their occlusion data)”, but there’s no explanation on how to actually implement it (hence me stating about no manual on implementing more “advanced” instancing features).

Finally, UnityInstancing.cginc actually has redefinitions of SH data to point to an array, just like with transform matricies, so that most certainly has to be working.

Hmm, you’re right. They’ve updated instancing w/ light probes since I last looked at this. Ideally there shouldn’t be anything you need to do to make instancing work then. The fact that it doesn’t seems like a bug.

Yeah, it really does sound like a bug. Guess I’ll write a bug report in case if I won’t find a solution in a couple of days.

Did you resolve this issue in the end? I also seem to have this issue still and was wondering if there is a fix.

If you add this to your shader

#pragma multi_compile _ LIGHTPROBE_SH

it should enable instancing for light probes

Thanks for your reply. I have tried adding it to the shader but it still will not instance together, see:

I don’t see INSTANCING_ON keyword there, did you enable GPU Instancing in your material?

here’s simple shader example with working instanced light probes

Shader "Unlit/lightprobes"
{
    Properties
    {
    }
    SubShader
    {
        Tags {
            "RenderType" = "Opaque"
            "LightMode" = "ForwardBase"
            }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma multi_compile _ LIGHTPROBE_SH

            #pragma multi_compile_instancing

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                UNITY_VERTEX_INPUT_INSTANCE_ID
                float4 vertex : POSITION;
                fixed3 normal : NORMAL;
            };

            struct v2f
            {
                UNITY_VERTEX_INPUT_INSTANCE_ID
                float4 vertex : SV_POSITION;
                fixed3 normal : NORMAL;
                fixed3 ambient : TEXCOORD0;
            };

            v2f vert (appdata v)
            {
                UNITY_SETUP_INSTANCE_ID(v);
                v2f o;
               
                // UNITY_TRANSFER_INSTANCE_ID(v, o); //if you need per pixel light probe lighting, uncomment this
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.normal = UnityObjectToWorldNormal(v.normal);

                o.ambient = ShadeSH9(fixed4(o.normal, 1));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //per pixel lighting
                // UNITY_SETUP_INSTANCE_ID(i);
                // fixed3 ambient = ShadeSH9(fixed4(i.normal, 1));
               
                fixed3 ambient = i.ambient;
                return fixed4(ambient, 1);
            }
            ENDCG
        }
    }
}