How to use LOD cross fade on sprite renderer?

Hello,

I’m working on an URP 2D Great Strategy game. I have a big map with envrionment sprites like trees, bushes, hills and mountains. Currently, I have nearly 4000 sprites set as GPU Instanced, static and without collider but they consumes really much of my frame.

That’s why I’m looking for displaying them only when I zoom. [the zoom is decreasing the orthographic size of the camera]. I want to use LOD Group component because Unity recognized the distance from the object even on the orthographic mode. This way I can display or not my sprites if I’m at the max dezoom or zoom.

But the LOD change is too brutal, it just culls the renderer and I want to make it fade (with dither, noise, or simply lerping the alpha). And it doesn’t seem to work with any of shader it tried: default Unlit/Lit, custom shader graph with custom crossfade node, custom shaderlab with explicit crossfade call.

Online resources are really not helpful, or too outdated. So, if anyone can help me understand what am I doing wrong? Thank you very much :folded_hands:

My LOD setup:

The Shader Graph I tried (following this thread: LOD CrossFade Shader in URP):

The Shader Lab I tried too:

Shader "Custom/SpriteMountainLOD"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma multi_compile_vertex LOD_FADE_PERCENTAGE LOD_FADE_CROSSFADE
            #pragma multi_compile_fragment __ LOD_FADE_CROSSFADE

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color: COLOR;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.color = v.color;
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv) * i.color;
                UNITY_APPLY_DITHER_CROSSFADE(i.vertex.xy);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

Bump!

I moved to a 2.5D system where I have 2D sprites rendered in a 3D world, and I need to cross-fade between my sprites (instead of the culling them with a enabled/disabled state).

I made a shader for my sprites but it still doesn’t work for SpriteRenderer… I also read this post saying the same : Unable to Crossfade SpriteRenderers using LODGroup?

Please, is there a fix somewhere? Or do I need to manually implement my own LOD Group to handle SpriteRenderer?

I believe 2D Renderers do not natively support LOD Group. Given the use-case, perhaps import Sprites as TightMesh, export geometry to Mesh assets and use MeshRenderers since its being used as Billboard as 3D World.
Kindly let us know if you need more info. Thanks.

Two ideas:

  1. Simply write the fade in your own shader. You can access the object position and the camera position in the shader and from there you can configure material properties for what distances you want to start and end your fade out.

  2. Don’t use SpriteRenderers. Manually render them using DrawMeshInstanced. 4000 sprites should be absolutely zero work for any desktop and probably won’t be too bad for most mobile devices if you keep the overdraw and vertex count reasonable. You can even combine this with the shader for maximum effect if you are targeting a particularly weak mobile platform.

There would be too much overhead using LOD groups. You should place the sprites on a layer and have the camera not render the layer when zoomed out. I would also combine all the sprites into a single mesh. Instancing isn’t really optimal for simple quads because it also has overheads.

When you do the above you’ll be free to simply fade out the sprites by changing their shared material’s alpha.