2
\$\begingroup\$

I'm trying to display several Images as billboards. All the Images are under the same Canvas. I'm using a billboard shader I've been working on:

Shader "UI/Unlit/Billboard Transparent Colored"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp] 
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            #pragma multi_compile __ UNITY_UI_ALPHACLIP

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(IN);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

                // Method 1
                /*
                float scaleX = length(mul(unity_ObjectToWorld, float4(1.0, 0.0, 0.0, 0.0)));
                float scaleY = length(mul(unity_ObjectToWorld, float4(0.0, 1.0, 0.0, 0.0)));
                OUT.vertex = mul(
                    UNITY_MATRIX_P,
                    float4(UnityObjectToViewPos(float3(0, 0, 0)), 1) + float4(IN.vertex.x * scaleX, IN.vertex.y * scaleY, 0, 0)
                    //mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0)) + float4(IN.vertex.x * scaleX, IN.vertex.y * scaleY, 0.0f, 0.0f)
                );
                */

                // Method 2
                float4x4 mv = UNITY_MATRIX_MV;

                mv[0][0] = 1.0;
                mv[0][1] = 0.0;
                mv[0][2] = 0.0;

                mv[1][0] = 0.0;
                mv[1][1] = 1.0;
                mv[1][2] = 0.0;

                mv[2][0] = 0.0;
                mv[2][1] = 0.0;
                mv[2][2] = 1.0;

                OUT.vertex = mul(
                    UNITY_MATRIX_P,
                    mul(mv, IN.vertex)
                );

                OUT.worldPosition = OUT.vertex;
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;

                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;    

                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif

                return color;
            }
        ENDCG
        }
    }
}

The problem is that when the Image is not at the center of the canvas (0,0,0), the billboard rotation is not around it's pivot, but the canvas pivot. Here is a small gif:

Billboard error

As you can see the Image on the left (2,0,0) is rotating around the same pivot of the left one (0,0,0), the Canvas pivot (0,0,0).

Here are some caps of the Images transforms and the Canvas transform:

Images transforms Canvas transform

I've tried several things but with no success. I'm still learning to programming shaders. Any suggestion is really welcome.

\$\endgroup\$
  • \$\begingroup\$ UI gets drawn as a batch when possible, so the translation component of the MV matrix will have a single value for all content in the batch, with each UI element's vertices relative to that origin. So a shader might not be the simplest way to perform this billboarding effect. Have you considered billboarding individual SpriteRenderers or textured quad MeshRenderers instead, since you're already overriding part of the layout functionality provided by the Canvas anyway? \$\endgroup\$ – DMGregory Sep 14 '17 at 22:04
  • \$\begingroup\$ I know it's a possible way to use several SpriteRenderers, quads or even several Canvas. The problem is that I also wanted to use the UI Texts, and a lot of them, with it's content varying frequently. Since it's mean to be used on every tile of a huge grid. Not just displaying textures or sprites. \$\endgroup\$ – Celtc Sep 15 '17 at 0:23
1
\$\begingroup\$

For optimization Unity usually batches meshes under certain circumstances. This messes up your reference system.

To prevent Unity from batching the meshes add this tag to any shader that breaks with batching:

"DisableBatching" = "True"
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.