Search Unity

  1. New Unity Live Help updates. Check them out here!

    Dismiss Notice

How to draw many circles efficiently

Discussion in 'Shaders' started by VictorKs, Oct 31, 2019.

  1. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    148
    What is the best way to draw thousands of circles with minimum cpu. I need them for a unity selection system they have to be rendered on units feet.
     
  2. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    188
    Example of rendering one milion blue circles with single draw call (add CS cript to main camera).

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class DrawCircles : MonoBehaviour
    4. {
    5.     public Shader shader;
    6.     protected Material material;
    7.    
    8.     void Start()
    9.     {
    10.         material = new Material(shader);
    11.     }
    12.  
    13.     void OnRenderObject()
    14.     {
    15.         material.SetPass(0);
    16.         Graphics.DrawProcedural(MeshTopology.Triangles, 6 * 1000000, 1);
    17.     }
    18. }
    19.  
    Shader:

    Code (CSharp):
    1. Shader "Draw Circles"
    2. {
    3.     Subshader
    4.     {  
    5.         Pass
    6.         {
    7.             Cull Off
    8.             CGPROGRAM
    9.             #pragma vertex VSMain
    10.             #pragma fragment PSMain
    11.             #pragma target 5.0
    12.  
    13.             float mod(float x, float y)
    14.             {
    15.                 return x - y * floor(x/y);
    16.             }          
    17.            
    18.             float4 VSMain (uint id:SV_VertexID, out float2 UV : TEXCOORD1) : SV_POSITION
    19.             {
    20.                 float q = floor(id / 6.0);
    21.                 float3 center = float3(mod(q,1000.0),0.0, floor(q/1000.0));
    22.                 center *= 2..xxx;
    23.                 if (mod(float(id),6)==0)
    24.                 {
    25.                     UV = float2(0,0);
    26.                     float3 worldPos = float3(-0.5,0,-0.5) + center;
    27.                     return UnityObjectToClipPos(float4(worldPos,1.0));
    28.                 }
    29.                 else if (mod(float(id),6)==1)
    30.                 {
    31.                     UV = float2(1,0);
    32.                     float3 worldPos = float3(0.5,0,-0.5) + center;
    33.                     return UnityObjectToClipPos(float4(worldPos,1.0));
    34.                 }
    35.                 else if (mod(float(id),6)==2)
    36.                 {
    37.                     UV = float2(0,1);
    38.                     float3 worldPos = float3(-0.5,0,0.5) + center;
    39.                     return UnityObjectToClipPos(float4(worldPos,1.0));
    40.                 }
    41.                 else if (mod(float(id),6)==3)
    42.                 {
    43.                     UV = float2(1,0);
    44.                     float3 worldPos = float3(0.5,0,-0.5) + center;
    45.                     return UnityObjectToClipPos(float4(worldPos,1.0));
    46.                 }
    47.                 else if (mod(float(id),6)==4)
    48.                 {
    49.                     UV = float2(1,1);
    50.                     float3 worldPos = float3(0.5,0,0.5) + center;
    51.                     return UnityObjectToClipPos(float4(worldPos,1.0));
    52.                 }
    53.                 else
    54.                 {
    55.                     UV = float2(0,1);
    56.                     float3 worldPos = float3(-0.5,0,0.5) + center;
    57.                     return UnityObjectToClipPos(float4(worldPos,1.0));
    58.                 }
    59.             }
    60.  
    61.             float4 PSMain (float4 vertex:SV_POSITION, float2 UV : TEXCOORD1) : SV_Target
    62.             {
    63.                 float2 S = UV*2.0-1.0;
    64.                 if (dot(S.xy, S.xy) > 1.0) discard;
    65.                 return float4(0,0,1,1);
    66.             }
    67.             ENDCG
    68.         }
    69.        
    70.     }
    71. }
    upload_2019-10-31_22-8-49.png
     
    VictorKs, Invertex and bgolus like this.
  3. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    148
    Thanks a lot for the reply I didn't know about procedural drawing. I thought about baking a quad on my characters meshes but this is much better!
     
  4. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    188
    I removed branching from vertex shader. Now, for VS, I have 24 math instructions and 2 temp registers. Previous example was related with calculating circles centers procedurally as grid of circles, now example with array:

    Code (CSharp):
    1. // Add script to camera and assign shader "DrawCircles".
    2. // Script renders 2048 circles with single draw call and their center coordinates
    3. // are calculated once on the CPU and sent to GPU array.
    4. // To use more than 2048 circles, you can use structured buffer, bake point data to texture,
    5. // or just calculate circles center coordinates procedurally (directly inside vertex shader).
    6. using UnityEngine;
    7. public class DrawCircles : MonoBehaviour
    8. {
    9.     public Shader shader;
    10.     protected Material material;
    11.     void Awake()
    12.     {
    13.         material = new Material(shader);
    14.         float[] bufferX = new float[2048];
    15.         float[] bufferY = new float[2048];
    16.         for (int i=0; i<2048; i++)
    17.         {
    18.             bufferX[i] = Random.Range(0.0f, 120.0f);
    19.             bufferY[i] = Random.Range(0.0f, 120.0f);
    20.         }
    21.         material.SetFloatArray("BufferX", bufferX);
    22.         material.SetFloatArray("BufferY", bufferY);
    23.     }
    24.     void OnRenderObject()
    25.     {
    26.         material.SetPass(0);
    27.         Graphics.DrawProcedural(MeshTopology.Triangles, 6, 2048);
    28.     }
    29. }
    Code (CSharp):
    1. Shader "Draw Circles"
    2. {
    3.     Subshader
    4.     {
    5.         Pass
    6.         {
    7.             Cull Off
    8.             CGPROGRAM
    9.             #pragma vertex VSMain
    10.             #pragma fragment PSMain
    11.             #pragma target 5.0
    12.  
    13.             float BufferX[2048];
    14.             float BufferY[2048];
    15.  
    16.             float mod(float x, float y)
    17.             {
    18.                 return x - y * floor(x/y);
    19.             }
    20.  
    21.             float3 hash(float p)
    22.             {
    23.                 float3 p3 = frac(p.xxx * float3(.1239, .1237, .2367));
    24.                 p3 += dot(p3, p3.yzx+63.33);
    25.                 return frac((p3.xxy+p3.yzz)*p3.zyx);
    26.             }
    27.  
    28.             float4 VSMain (uint id:SV_VertexID, out float2 uv:TEXCOORD0, inout uint instance:SV_INSTANCEID) : SV_POSITION
    29.             {
    30.                 float3 center = float3(BufferX[instance], 0.0, BufferY[instance]);
    31.                 float u = mod(float(id),2.0);
    32.                 float v = sign(mod(126.0,mod(float(id),6.0)+6.0));
    33.                 uv = float2(u,v);
    34.                 return UnityObjectToClipPos(float4(float3(sign(u)-0.5, 0.0, sign(v)-0.5) + center,1.0));
    35.             }
    36.  
    37.             float4 PSMain (float4 vertex:SV_POSITION, float2 uv:TEXCOORD0, uint instance:SV_INSTANCEID) : SV_Target
    38.             {
    39.                 float2 S = uv*2.0-1.0;
    40.                 if (dot(S.xy, S.xy) > 1.0) discard;
    41.                 return float4(hash(float(instance)), 1.0);
    42.             }
    43.             ENDCG
    44.         }
    45.     }
    46. }
    upload_2019-11-4_15-58-15.png
     
    addent likes this.
unityunity