My shader doesn't work if I use the same material for multiple objects with the same texture.

Hey, everyone!

I am very new to shaders and I tried making a billboard shader. I used the code from [this video] (Unity Shader Tutorial - Billboard Shader - YouTube)

The shader works fine if there is only one object with a material from this shader. It stays in the same position and looks at the camera as the camera rotates , it is the effect I wanted. But:

  • If I create another object with the same sprite and the same material, in certain camera angles sprites get rendered at very far positions out of the cameras view range.
  • If I create another object with the same sprite and use another material I created from this shader, It also works fine.
  • I tried using material property block and used that for different objects with the same sprite and the same material, it only works if the property values different from each other, if they are the same it doesn’t work either.
  • I am using Sprite renderer, when I tried using mesh renderer it didn’t have this problem and worked fine, but I want to use Sprite renderer and really wondering what is the issue here?
  • Also the sprites I am using has transparent parts, but I am not sure it has anything to do with this problem. I come across [this thread about alpha blended tree billboards] (https://forum.unity.com/threads/transparency-and-tree-billboards.36404/) and I am not sure if it is a " alpha blended materials don’t sort properly problem"

Any help greatly appreciated.

Here is the shader code :

 Shader "Shaders/UnlitBillBoard"
{
Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    
    [PerRendererData]_ScaleFactor("Scale Factor" , Range(0,1)) = 0

    [PerRendererData]_ReferenceDistance("Referance Distance", Float) = 10
}
SubShader
{
    Tags { "RenderType"="Transparent" "Queue" = "Transparent+500" }
    LOD 100

    Pass
    {
        ZTest Off
        Blend SrcAlpha OneMinusSrcAlpha
        Cull Off

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // make fog work
        #pragma multi_compile_fog

        #include "UnityCG.cginc"

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

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

        sampler2D _MainTex;
        float4 _MainTex_ST;
        float _ScaleFactor;
        float _ReferenceDistance;

        v2f vert (appdata v)
        {
            v2f o;
            //o.vertex = UnityObjectToClipPos(v.vertex);

            float4 world_origin = mul(UNITY_MATRIX_M ,float4(0,0,0,1));
            float4 view_origin = float4(UnityObjectToViewPos(float3(0,0,0)),1);

            float scale = (_ScaleFactor * (length(view_origin) / _ReferenceDistance)) + ((1 - _ScaleFactor));

            float4 world_pos = mul(UNITY_MATRIX_M , float4(scale,scale,scale,1) *  v.vertex); 
            float4 flipped_world_pos= float4(-1,1,-1,1) * world_pos - world_origin + view_origin;
            //float4 view_pos = mul(UNITY_MATRIX_V,world_pos);
            float4 view_pos = world_pos - world_origin + view_origin;
            float4 clip_pos = mul(UNITY_MATRIX_P,view_pos);

            o.vertex = clip_pos;
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            UNITY_TRANSFER_FOG(o,o.vertex);
            return o;
        }

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

}

If I create another object with the
same sprite and the same material, in
certain camera angles sprites get
rendered at very far positions out of
the cameras view range.

This sounds like a problem involving dynamic batching. In short, the world positions of the vertices become their local positions, so there is only a single origin point at (0, 0, 0) for all the billboard-shader-rendered objects.

For reference, in your line:

float4 world_origin = mul(UNITY_MATRIX_M ,float4(0,0,0,1));

float4(0,0,0,1) represents the object’s origin point when multiplied by the Model Matrix (UNITY_MATRIX_M), where vertex positions (float4 [name]: POSITION) are the local positions of the vertices.

(Multiplying by the Model Matrix is simply one of the key steps in preparing the mesh to be rendered to the screen)

For this particular shader, you probably just want to disable the dynamic batching, by adding "DisableBatching" = "True" to your tags.

Tags { "RenderType"="Transparent" "Queue" = "Transparent+500" "DisableBatching" = "True"}

Edit: Added more details