Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Question Billboard shader for GPU Instancing

Discussion in 'Shaders' started by dccoo, Sep 24, 2022.

  1. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    157
    I'm trying to use GPU instancing to spawn some billboard elements in my screen, but the billboard shader isn't working.

    I'm using the shader code provided by Unity here as a starting point. I modified it and now it is only spawning the objects at the given world position
    data.xyz
    .

    This is the current state of the shader code:

    Code (CSharp):
    1. Shader "Instanced/InstancedShader" {
    2.  
    3.     Properties{
    4.         _MainTex("Albedo (RGB)", 2D) = "white" {}
    5.     }
    6.  
    7.     SubShader{
    8.  
    9.         Pass {
    10.  
    11.             Tags {"LightMode" = "SRPDefaultUnlit"}
    12.  
    13.             CGPROGRAM
    14.  
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
    18.             #pragma target 4.5
    19.  
    20.             #include "UnityCG.cginc"
    21.             #include "UnityLightingCommon.cginc"
    22.             #include "AutoLight.cginc"
    23.  
    24.             sampler2D _MainTex;
    25.  
    26.         #if SHADER_TARGET >= 45
    27.             StructuredBuffer<float4> positionBuffer;
    28.         #endif
    29.  
    30.             struct v2f
    31.             {
    32.                 float4 pos : SV_POSITION;
    33.                 float2 uv : TEXCOORD0;
    34.                 SHADOW_COORDS(4)
    35.             };
    36.  
    37.             v2f vert(appdata_full v, uint instanceID : SV_InstanceID)
    38.             {
    39.             #if SHADER_TARGET >= 45
    40.                 float4 data = positionBuffer[instanceID];
    41.             #else
    42.                 float4 data = 0;
    43.             #endif
    44.                 float3 localPosition = v.vertex.xyz;
    45.                 float3 worldPosition = data.xyz + localPosition;
    46.                 float3 worldNormal = v.normal;
    47.  
    48.                 v2f o;
    49.                 o.pos = mul(UNITY_MATRIX_VP, float4(outPos, 1.0f));
    50.                 o.uv = v.texcoord;                
    51.                 TRANSFER_SHADOW(o)
    52.                 return o;
    53.             }
    54.  
    55.             fixed4 frag(v2f i) : SV_Target
    56.             {
    57.                 return tex2D(_MainTex, i.uv);;
    58.             }
    59.  
    60.             ENDCG
    61.         }
    62.     }
    63. }
    How can I make a billboard out from this shader? I've been trying a bunch of stuff but in the end the instantiated meshes always look wierd or simply disappear or spawn in the wrong positions (they should be centered in data.xyz).
     
  2. Molgerax

    Molgerax

    Joined:
    Jul 24, 2020
    Posts:
    5
    I have been working on a custom particle system too, and the solution I use for now is instancing a mesh with a single vertex at position (0,0,0) and using a geometry shader to generate the billboard from that. I haven't looked at this code in quite a while, so it can probably be optimized, or steps could be skipped, but it works for me. At least this should give you a jumping off point, someone else might have something more elegant than this.

    Code (csharp):
    1.  
    2. v2g vert(inputVert i)
    3. {
    4.    v2g o;
    5.  
    6.    //  Different instancing setup, but that shouldnt matter
    7.    UNITY_INITIALIZE_OUTPUT(v2g, o);
    8.    UNITY_SETUP_INSTANCE_ID(i);
    9.    UNITY_TRANSFER_INSTANCE_ID(i, o);
    10.  
    11.    #if defined(ENABLE_INSTANCING)
    12.    ParticleData particle = _ParticleDataBuffer[unity_InstanceID];
    13.    // No transform matrix here, happens in the geometry shader!
    14.    o.vertex = float4(particle.worldPosition, 1);
    15.    o.color = float4(particle.albedo, 1);
    16.  
    17.    #else
    18.    o.vertex = i.vertex;
    19.    o.color = float4(1, 0, 1, 1);
    20.    #endif
    21.    return o;
    22. }
    23.  
    24.  
    25. [maxvertexcount(4)]
    26. void geom(point v2g IN[1], inout TriangleStream<g2f> triStream)
    27. {
    28.    g2f o;
    29.  
    30.    UNITY_INITIALIZE_OUTPUT(g2f, o);
    31.    UNITY_SETUP_INSTANCE_ID(IN[0]);
    32.    UNITY_TRANSFER_INSTANCE_ID(IN[0], o);
    33.  
    34.      
    35.    float4 vert;
    36.  
    37.    o.color = IN[0].color;
    38.  
    39.    // Make a quad out of the input position. We add an offset of 0.5 in local space to each corner so a quad is 1 with
    40.    //_BillboardScale of 1 is 1 unit tall/wide. We need to multiply that offset vector with the model-view matrix in reverse order,
    41.    // so that the perspective skewing from UnityObjectToClipPos is cancelled later. I guess. Kinda forgot.
    42.  
    43.    vert = IN[0].vertex + mul(float4(-0.5, -0.5, 0, 0) * _BillboardScale, UNITY_MATRIX_MV);
    44.    o.clipPos = UnityObjectToClipPos(vert);
    45.    o.uv = float2(0, 0);
    46.    triStream.Append(o);
    47.  
    48.    vert = IN[0].vertex + mul(float4(-0.5, 0.5, 0, 0) * _BillboardScale, UNITY_MATRIX_MV);
    49.    o.clipPos = UnityObjectToClipPos(vert);
    50.    o.uv = float2(0, 1);
    51.    triStream.Append(o);
    52.  
    53.    vert = IN[0].vertex + mul(float4(0.5, -0.5, 0, 0) * _BillboardScale, UNITY_MATRIX_MV);
    54.    o.clipPos = UnityObjectToClipPos(vert);
    55.    o.uv = float2(1, 0);
    56.    triStream.Append(o);
    57.  
    58.    vert = IN[0].vertex + mul(float4(0.5, 0.5, 0, 0) * _BillboardScale, UNITY_MATRIX_MV);
    59.    o.clipPos = UnityObjectToClipPos(vert);
    60.    o.uv = float2(1, 1);
    61.    triStream.Append(o);
    62.  
    63.    triStream.RestartStrip();
    64. }
    65.  
    66. float4 frag(g2f i) : SV_Target
    67. {
    68.    UNITY_SETUP_INSTANCE_ID(i);
    69.  
    70.    float3 col = i.color * tex2D(_MainTex, i.uv);
    71.    return float4(col, 1);
    72. }
    73.  
    74.