120 + рдПрдлрдкреАрдПрд╕ рдкрд░ рдорд┐рд▓рд┐рдпрди рд╕реНрдкреНрд░рд╛рдЗрдЯреНрд╕

рдЫрд╡рд┐

рдпрджрд┐ рдЖрдк рдбреЙрдЯреНрд╕ рдлреЛрд░рдо рдХреЗ рдЖрд╕рдкрд╛рд╕ рдШреВрдорддреЗ рд╣реИрдВ , рддреЛ рдЖрдк рд╡рд╣рд╛рдВ рдЙрд╕реА рддрд░рд╣ рдХреА рдкреЛрд╕реНрдЯ рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреИрд╕реЗ рд▓реЗрдЦрдХ рдиреЗ рдПрдХ рд▓рд╛рдЦ рдПрдирд┐рдореЗрдЯреЗрдб рд╕реНрдкреНрд░рд╛рдЗрдЯреНрд╕ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдПрдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд▓рд┐рдЦрд╛ рдерд╛, рдФрд░ рдЕрднреА рднреА рдХреЗрд╡рд▓ 60fps рдорд┐рд▓рддрд╛ рд╣реИред рдореИрдВрдиреЗ рдЕрдкрдирд╛ рдЦреБрдж рдХрд╛ рдбреЙрдЯреНрд╕ рд╕реНрдкреНрд░рд╛рдЗрдЯ рд░реЗрдВрдбрд░ рдмрдирд╛рдпрд╛ , рдЬреЛ рд╣рдорд╛рд░реЗ рдЦреЗрд▓ рдХреЗ рд▓рд┐рдП рдХрд╛рдлреА рдЕрдЪреНрдЫрд╛ рд╣реИ , рд▓реЗрдХрд┐рди рдпрд╣ рдПрдХ рдорд┐рд▓рд┐рдпрди рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реИред рдореИрдВ рдЙрддреНрд╕реБрдХ рдерд╛ред

рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЛ рдлреЛрд░реНрдХ рдХрд┐рдпрд╛ рдФрд░ рдпрд╣ рдЬрд╛рдВрдЪрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдПрдХреЗрдбреЗрдорд┐рдпрд╛ рдореЗрдВ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдореИрдВрдиреЗ рдЙрд╕рдХреЗ рд╕рд╛рде рдереЛрдбрд╝рд╛ рдкреНрд░рдпреЛрдЧ рдХрд┐рдпрд╛, рджреЗрдЦрд╛ рдХрд┐ рдХреИрд╕реЗ рд╡рд╣ рдПрдХ рд╕реНрдкреНрд░рд╛рдЗрдЯ, рдПрдХ рд╕реМ, рдлрд┐рд░ рд╣рдЬрд╛рд░реЛрдВ рдХрд╛ рдкреНрд░рддрд┐рдкрд╛рджрди рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рд╡рд╣ рд╣рдорд╛рд░реЗ рдЦреЗрд▓ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХреЗ рд▓рд┐рдП рдмрд┐рд▓реНрдХреБрд▓ рддреИрдпрд╛рд░ рдирд╣реАрдВ рдерд╛ред рдЗрд╕рдореЗрдВ рдХреБрдЫ рдкрд╣рд▓реБрдУрдВ рдХрд╛ рдЕрднрд╛рд╡ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдкреАрдЫреЗ рд╕реЗ рд╕рд╛рдордиреЗ рддрдХ рдЫрдВрдЯрд╛рдИред рдореИрдВрдиреЗ рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рд╣реИрдХ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд┐рдпрд╛ред рдЬрдм рдореИрдВрдиреЗ рдХреЛрдб рдкрдврд╝рд╛, рддреЛ рдореИрдВрдиреЗ рдорд╣рд╕реВрд╕ рдХрд┐рдпрд╛ рдХрд┐ рдпрд╣ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдирдИ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд╛рдпрдХ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдЬрд┐рд╕рдХрд╛ рд╣рдо рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдореБрдЭреЗ рд╕рд┐рд░реНрдл рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рд╣реИ рдХрд┐ рдпрд╣ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреИрд╕реЗ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВ рдкрд╣рд▓реЗ рд╣реА рд╕рд┐рджреНрдзрд╛рдВрдд рдХреЛ рд╕рдордЭ рдЧрдпрд╛ рдерд╛ред

рдореВрд▓ рдмрд╛рддреЗрдВ


рдпрджрд┐ рдореИрдВ рдЗрд╕ рдкреНрд░рддрд┐рдкрд╛рджрди рддрдХрдиреАрдХ рдХреЛ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рддреЛ рдореБрдЭреЗ рд╕рдмрд╕реЗ рд╕рд░рд▓ рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ: рдПрдХ рдЕрд▓рдЧ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рдирд╛ред рдкреБрд╕реНрддрдХрд╛рд▓рдп ComputeBuffers рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рдЙрдиреНрд╣реЗрдВ рдХрдореНрдкреНрдпреВрдЯреЗрд╢рдирд▓ рд╢реЗрдбреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЬреАрдкреАрдпреВ рдореЗрдВ рдХрдореНрдкреНрдпреВрдЯреЗрд╢рди рдЯреНрд░рд╛рдВрд╕рдлрд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдореБрдЭреЗ рдирд╣реАрдВ рдкрддрд╛ рдерд╛ рдХрд┐ рдПрдХ рдирд┐рдпрдорд┐рдд рд╢реЗрдбрд░ рдореЗрдВ рдХреНрдпрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬреЛ рд╕реНрдХреНрд░реАрди рдкрд░ рдХреБрдЫ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИред рдЖрдк рдЙрдиреНрд╣реЗрдВ рдЙрди рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреЗ рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рд╕рд╛рдордЧреНрд░реА рдХреЛ рд╕реМрдВрдкрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдЬрд┐рд╕рдХреЗ рдмрд╛рдж shader рдЗрди рд╕рд╛рдордЧреНрд░рд┐рдпреЛрдВ рддрдХ рдкрд╣реБрдБрдЪ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рдЖрдк рд╕реНрдерд┐рддрд┐, рд░реЛрдЯреЗрд╢рди, рд╕реНрдХреЗрд▓, uv-рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ, рд░рдВрдЧ - рдЬреЛ рднреА рдЖрдк рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдЬреИрд╕реЗ рдбреЗрдЯрд╛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдиреАрдЪреЗ рдЗрд╕ рднрдпрд╛рдирдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рд╕рдВрд╢реЛрдзрд┐рдд рдПрдХ shader рд╣реИ:

  Shader "Instanced/ComputeBufferSprite" {
    Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    
    SubShader {
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
        }
        Cull Back
        Lighting Off
        ZWrite On
        Blend One OneMinusSrcAlpha
        Pass {
            CGPROGRAM
            // Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it uses non-square matrices
            #pragma exclude_renderers gles

            #pragma vertex vert
            #pragma fragment frag
            #pragma target 4.5

            #include "UnityCG.cginc"

            sampler2D _MainTex;

            // xy for position, z for rotation, and w for scale
            StructuredBuffer<float4> transformBuffer;

            // xy is the uv size, zw is the uv offset/coordinate
            StructuredBuffer<float4> uvBuffer; 

	        StructuredBuffer<float4> colorsBuffer;

            struct v2f{
                float4 pos : SV_POSITION;
                float2 uv: TEXCOORD0;
		        fixed4 color : COLOR0;
            };

            float4x4 rotationZMatrix(float zRotRadians) {
                float c = cos(zRotRadians);
                float s = sin(zRotRadians);
                float4x4 ZMatrix  = 
                    float4x4( 
                       c,  -s, 0,  0,a
                       s,  c,  0,  0,
                       0,  0,  1,  0,
                       0,  0,  0,  1);
                return ZMatrix;
            }

            v2f vert (appdata_full v, uint instanceID : SV_InstanceID) {
                float4 transform = transformBuffer[instanceID];
                float4 uv = uvBuffer[instanceID];
                
                //rotate the vertex
                v.vertex = mul(v.vertex - float4(0.5, 0.5, 0,0), rotationZMatrix(transform.z));
                
                //scale it
                float3 worldPosition = float3(transform.x, transform.y, -transform.y/10) + (v.vertex.xyz * transform.w);
                
                v2f o;
                o.pos = UnityObjectToClipPos(float4(worldPosition, 1.0f));
                
                // XY here is the dimension (width, height). 
                // ZW is the offset in the texture (the actual UV coordinates)
                o.uv =  v.texcoord * uv.xy + uv.zw;
                
		        o.color = colorsBuffer[instanceID];
                return o;
            }

            fixed4 frag (v2f i) : SV_Target{
                fixed4 col = tex2D(_MainTex, i.uv) * i.color;
				clip(col.a - 1.0 / 255.0);
                col.rgb *= col.a;

				return col;
            }

            ENDCG
        }
    }
}

рд╡реИрд░рд┐рдПрдмрд▓ рд╡реЗрд░рд┐рдПрдмрд▓ рдЯреНрд░рд╛рдВрд╕рдлрд╝реЙрд░реНрдордмрдлрд╝рд░, рдпреВрд╡рд╛рдлрд╝рд░ рдФрд░ рдХрд▓рд░реНрд╕рдмрдлрд╝рд░ "рдПрд░реЗрдЬрд╝" рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдо рдХрдореНрдкреНрдпреВрдЯреНрдпреВрдЯрд░реНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХреЛрдб рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВред рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреЛ рд░реЗрдВрдбрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ (рдЕрднреА рдХреЗ рд▓рд┐рдП) рдпрд╣ рд╕рдм рдЪрд╛рд╣рд┐рдПред рдпрд╣рд╛рдБ рдПрдХрд▓ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреЗ рдкреНрд░рддрд┐рдкрд╛рджрди рдХреЗ рд▓рд┐рдП рдореЛрдиреЛрдмреАрд╣реЙрд░ рд▓рд┐рдкрд┐ рд╣реИ:

public class ComputeBufferBasic : MonoBehaviour {
    [SerializeField]
    private Material material;

    private Mesh mesh;
    
    // Transform here is a compressed transform information
    // xy is the position, z is rotation, w is the scale
    private ComputeBuffer transformBuffer;
    
    // uvBuffer contains float4 values in which xy is the uv dimension and zw is the texture offset
    private ComputeBuffer uvBuffer;
    private ComputeBuffer colorBuffer;

    private readonly uint[] args = {
        6, 1, 0, 0, 0
    };
    
    private ComputeBuffer argsBuffer;

    private void Awake() {
        this.mesh = CreateQuad();
        
        this.transformBuffer = new ComputeBuffer(1, 16);
        float scale = 0.2f;
        this.transformBuffer.SetData(new float4[]{ new float4(0, 0, 0, scale) });
        int matrixBufferId = Shader.PropertyToID("transformBuffer");
        this.material.SetBuffer(matrixBufferId, this.transformBuffer);
        
        this.uvBuffer = new ComputeBuffer(1, 16);
        this.uvBuffer.SetData(new float4[]{ new float4(0.25f, 0.25f, 0, 0) });
        int uvBufferId = Shader.PropertyToID("uvBuffer");
        this.material.SetBuffer(uvBufferId, this.uvBuffer);
        
        this.colorBuffer = new ComputeBuffer(1, 16);
        this.colorBuffer.SetData(new float4[]{ new float4(1, 1, 1, 1) });
        int colorsBufferId = Shader.PropertyToID("colorsBuffer");
        this.material.SetBuffer(colorsBufferId, this.colorBuffer);

        this.argsBuffer = new ComputeBuffer(1, this.args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
        this.argsBuffer.SetData(this.args);
    }

    private static readonly Bounds BOUNDS = new Bounds(Vector2.zero, Vector3.one);

    private void Update() {   
        // Draw
        Graphics.DrawMeshInstancedIndirect(this.mesh, 0, this.material, BOUNDS, this.argsBuffer);
    }
    
    // This can be refactored to a utility class
    // Just added it here for the article
    private static Mesh CreateQuad() {
        Mesh mesh = new Mesh();
        Vector3[] vertices = new Vector3[4];
        vertices[0] = new Vector3(0, 0, 0);
        vertices[1] = new Vector3(1, 0, 0);
        vertices[2] = new Vector3(0, 1, 0);
        vertices[3] = new Vector3(1, 1, 0);
        mesh.vertices = vertices;

        int[] tri = new int[6];
        tri[0] = 0;
        tri[1] = 2;
        tri[2] = 1;
        tri[3] = 2;
        tri[4] = 3;
        tri[5] = 1;
        mesh.triangles = tri;

        Vector3[] normals = new Vector3[4];
        normals[0] = -Vector3.forward;
        normals[1] = -Vector3.forward;
        normals[2] = -Vector3.forward;
        normals[3] = -Vector3.forward;
        mesh.normals = normals;

        Vector2[] uv = new Vector2[4];
        uv[0] = new Vector2(0, 0);
        uv[1] = new Vector2(1, 0);
        uv[2] = new Vector2(0, 1);
        uv[3] = new Vector2(1, 1);
        mesh.uv = uv;

        return mesh;
    }
}

рдЖрдЗрдП рдЗрд╕ рдХреЛрдб рдХреЛ рдХреНрд░рдо рдореЗрдВ рд▓реЗрддреЗ рд╣реИрдВред рд╕рд╛рдордЧреНрд░реА рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдПрдХ рдирдИ рд╕рд╛рдордЧреНрд░реА рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдФрд░ рдлрд┐рд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рдКрдкрд░ рдмрддрд╛рдП рдЧрдП рд╢реЗрдбрд░ рдХреЛ рд╕реЗрдЯ рдХрд░реЗрдВред рдЗрд╕реЗ рдПрдХ рдмрдирд╛рд╡рдЯ / рд╕реНрдкреНрд░рд╛рдЗрдЯ рд╢реАрдЯ рдЕрд╕рд╛рдЗрди рдХрд░реЗрдВред рдореИрдВ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд╕реЗ рдПрдХ рд╕реНрдкреНрд░рд╛рдЗрдЯ рд╢реАрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВ, рдЬреЛ рдХрд┐ 4x4 рд╕реНрдкреНрд░рд╛рдЗрдЯ рдЗрдореЛрдЬреА рдЖрдЗрдХрди рд╣реИред


рдпрд╣рд╛рдБ рдореЗрд╖ CreateQuad () рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдИ рдЧрдИ рдЬрд╛рд▓реА рд╣реИред рдпрд╣ рд╕рд┐рд░реНрдл рдПрдХ рдЪрддреБрд░реНрднреБрдЬ рд╣реИ рдЬреЛ рджреЛ рддреНрд░рд┐рдХреЛрдгреЛрдВ рд╕реЗ рдмрдирд╛ рд╣реИред рдЗрд╕рдХреЗ рдмрд╛рдж рддреАрди ComputeBuffer рд╡реИрд░рд┐рдПрдмрд▓ рд╣реИрдВ, рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдо рдмрд╛рдж рдореЗрдВ рдЗрд╕рдХреЗ рд▓рд┐рдП рд╕рд╛рдордЧреНрд░реА рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВрдЧреЗред рдореИрдВрдиреЗ рдЙрдиреНрд╣реЗрдВ рдЙрд╕реА рддрд░рд╣ рдирд╛рдорд┐рдд рдХрд┐рдпрд╛ рдЬреИрд╕реЗ рд╕реНрдЯреНрд░рдХреНрдЪрд░реНрдбрдмрдлрд░ рдЪрд░ рдореЗрдВ рдЫрд╛рдпрд╛рджрд╛рд░ред рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИред

Args рдФрд░ argsBuffer рд╡реИрд░рд┐рдПрдмрд▓реНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ Graphics.DrawMeshInstancedIndirect () рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдкреНрд░рд▓реЗрдЦрди рдпрд╣рд╛рдБ рд╣реИ ред рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдкрд╛рдВрдЪ рдпреВрдЗрдВрдЯ рд╡реИрд▓реНрдпреВ рд╡рд╛рд▓реЗ рдмрдлрд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХреЗрд╡рд▓ рдкрд╣рд▓реЗ рджреЛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИрдВред рдкрд╣рд▓рд╛ рд╕реВрдЪрдХрд╛рдВрдХреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИ, рдФрд░ рд╣рдорд╛рд░реА рдЪрддреБрд░реНрднреБрдЬ рдХреЗ рд▓рд┐рдП рдпрд╣ 6 рд╣реИред рджреВрд╕рд░рд╛ рдпрд╣ рд╣реИ рдХрд┐ рдЪрддреБрд░реНрднреБрдЬ рдХрд╛ рдкреНрд░рддрд┐рдкрд╛рджрди рдХрд┐рддрдиреА рдмрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдпрд╣ рд╕рд┐рд░реНрдл 1 рд╣реИред рдореИрдВ рдЗрд╕реЗ рдЕрдзрд┐рдХрддрдо рд╢реЗрдпрд░реНрдб рджреНрд╡рд╛рд░рд╛ рдЗрдВрдбреЗрдХреНрд╕ рд╕реНрдЯреНрд░рдХреНрдЪрд░реНрдбрдмрдлрд╝рд░ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдореВрд▓реНрдп рдХреЗ рд░реВрдк рдореЗрдВ рднреА рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реВрдВред рдЙрд╕рдХреЗ рдЬреИрд╕рд╛:

for(int i = 0; i < count; ++i) {
    CallShaderUsingThisIndexForBuffers(i);
}

рд╕рдЬрдЧ () рд╡рд┐рдзрд┐ рдХреЗрд╡рд▓ рд╕рд╛рдордЧреНрд░реА рдЕрд╕рд╛рдЗрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП ComputeBuffers рддреИрдпрд╛рд░ рдХрд░ рд░рд╣реА рд╣реИред рд╣рдо 0.2f рдХреЗ рдкреИрдорд╛рдиреЗ рдХреЗ рд╕рд╛рде рдФрд░ рд░реЛрдЯреЗрд╢рди рдХреЗ рдмрд┐рдирд╛ рдмрд┐рдВрджреБ (0, 0) рдкрд░ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддреЗ рд╣реИрдВред рдпреВрд╡реА рдХреЗ рд▓рд┐рдП, рд╣рдо рдирд┐рдЪрд▓реЗ рдмрд╛рдПрдБ рдХреЛрдиреЗ (рдЪреБрдВрдмрди рдЗрдореЛрдЬреА) рдореЗрдВ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдлрд┐рд░ рд╣рдо рд╕рдлреЗрдж рд░рдВрдЧ рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред рдЖрд░реНрдЧреНрд╕ рд╕рд░рдгреА argsBuffer рдкрд░ рд╕реЗрдЯ рд╣реИред

рдЕрдкрдбреЗрдЯ () рдореЗрдВ, рд╣рдо рдмрд╕ Graphics.DrawMeshInstancedIndirect () рдХрд╣рддреЗ рд╣реИрдВред (рдореБрдЭреЗ рдЕрднреА рддрдХ рдпрд╣ рд╕рдордЭ рдореЗрдВ рдирд╣реАрдВ рдЖрдпрд╛ рд╣реИ рдХрд┐ рдпрд╣рд╛рдВ BOUNDS рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдХреЗрд╡рд▓ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд╕реЗ рдХреЙрдкреА рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред)

рдЕрдВрддрд┐рдо рдЪрд░рдг рдПрдХ рдСрд░реНрдереЛрдЧреЛрдирд▓ рдХреИрдорд░рд╛ рдХреЗ рд╕рд╛рде рдПрдХ рджреГрд╢реНрдп рддреИрдпрд╛рд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдПрдХ рдФрд░ GameObject рдмрдирд╛рдПрдБ рдФрд░ ComputeBufferBasic рдШрдЯрдХ рдЬреЛрдбрд╝реЗрдВред рдЪрд▓реЛ рдЙрд╕реЗ рджрд┐рдЦрд╛рддреЗ рд╣реБрдП shader рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕рд╛рдордЧреНрд░реА рд╕реЗрдЯ рдХрд░реЗрдВред рд╕реНрдЯрд╛рд░реНрдЯрдЕрдк рдкрд░, рд╣рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдорд┐рд▓рддреЗ рд╣реИрдВ:


рдЕрд░реЗ рд╣рд╛рдВ! ComputeBuffer рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛ рдЧрдпрд╛ред

рдпрджрд┐ рдЖрдк рдПрдХ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдмрд╣реБрдд рдХреБрдЫ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ


рдЕрдм рдЬрдм рд╣рдордиреЗ рд╕реАрдЦрд╛ рд╣реИ рдХрд┐ ComputeBuffers рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреЛ рдХреИрд╕реЗ рд░реЗрдВрдбрд░ рдХрд┐рдпрд╛ рдЬрд╛рдП, рддреЛ рд╣рдо рдмрд╣реБрдд рдХреБрдЫ рдЖрдХрд░реНрд╖рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣рд╛рдБ рдПрдХ рдФрд░ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдмрдирд╛рдИ рдЧрдИ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдПрдХ рдорд╛рддреНрд░рд╛ рдкреИрд░рд╛рдореАрдЯрд░ рд╣реИ рдФрд░ рдПрдХ рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рд╕реНрдерд┐рддрд┐, рдкреИрдорд╛рдиреЗ, рд░реЛрдЯреЗрд╢рди рдФрд░ рд░рдВрдЧ рдХреЗ рд╕рд╛рде рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреА рдирд┐рд░реНрджрд┐рд╖реНрдЯ рд╕рдВрдЦреНрдпрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ:

public class ComputeBufferMultipleSprites : MonoBehaviour {
    [SerializeField]
    private Material material;
    
    [SerializeField]
    private float minScale = 0.15f;
    
    [SerializeField]
    private float maxScale = 0.2f;  

    [SerializeField]
    private int count;

    private Mesh mesh;
    
    // Matrix here is a compressed transform information
    // xy is the position, z is rotation, w is the scale
    private ComputeBuffer transformBuffer;
    
    // uvBuffer contains float4 values in which xy is the uv dimension and zw is the texture offset
    private ComputeBuffer uvBuffer;
    private ComputeBuffer colorBuffer;

    private uint[] args;
    
    private ComputeBuffer argsBuffer;

    private void Awake() {
        QualitySettings.vSyncCount = 0;
        Application.targetFrameRate = -1;
        
        this.mesh = CreateQuad();
        
        // Prepare values
        float4[] transforms = new float4[this.count];
        float4[] uvs = new float4[this.count];
        float4[] colors = new float4[this.count];

        const float maxRotation = Mathf.PI * 2;
        for (int i = 0; i < this.count; ++i) {
            // transform
            float x = UnityEngine.Random.Range(-8f, 8f);
            float y = UnityEngine.Random.Range(-4.0f, 4.0f);
            float rotation = UnityEngine.Random.Range(0, maxRotation);
            float scale = UnityEngine.Random.Range(this.minScale, this.maxScale);
            transforms[i] = new float4(x, y, rotation, scale);
            
            // UV
            float u = UnityEngine.Random.Range(0, 4) * 0.25f;
            float v = UnityEngine.Random.Range(0, 4) * 0.25f;
            uvs[i] = new float4(0.25f, 0.25f, u, v);
            
            // color
            float r = UnityEngine.Random.Range(0f, 1.0f);
            float g = UnityEngine.Random.Range(0f, 1.0f);
            float b = UnityEngine.Random.Range(0f, 1.0f);
            colors[i] = new float4(r, g, b, 1.0f);
        }
        
        this.transformBuffer = new ComputeBuffer(this.count, 16);
        this.transformBuffer.SetData(transforms);
        int matrixBufferId = Shader.PropertyToID("transformBuffer");
        this.material.SetBuffer(matrixBufferId, this.transformBuffer);
        
        this.uvBuffer = new ComputeBuffer(this.count, 16);
        this.uvBuffer.SetData(uvs);
        int uvBufferId = Shader.PropertyToID("uvBuffer");
        this.material.SetBuffer(uvBufferId, this.uvBuffer);
        
        this.colorBuffer = new ComputeBuffer(this.count, 16);
        this.colorBuffer.SetData(colors);
        int colorsBufferId = Shader.PropertyToID("colorsBuffer");
        this.material.SetBuffer(colorsBufferId, this.colorBuffer);

        this.args = new uint[] {
            6, (uint)this.count, 0, 0, 0
        };
        this.argsBuffer = new ComputeBuffer(1, this.args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
        this.argsBuffer.SetData(this.args);
    }

    private static readonly Bounds BOUNDS = new Bounds(Vector2.zero, Vector3.one);

    private void Update() {   
        // Draw
        Graphics.DrawMeshInstancedIndirect(this.mesh, 0, this.material, BOUNDS, this.argsBuffer);
    }

    private static Mesh CreateQuad() {
        // Just the same as previous code. I told you this can be refactored.
    }
}

рдПрдХрд▓ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреЗ рдкреНрд░рддрд┐рдкрд╛рджрди рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рдХреЛрдИ рдкрд░рд┐рд╡рд░реНрддрди рдирд╣реАрдВ рд╣реИрдВред рдЕрдВрддрд░ рдпрд╣ рд╣реИ рдХрд┐ рдЕрдм рд╣рдо рдХреНрд░рдордмрджреНрдз рдЪрд░ рдЧрдгрдирд╛ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдПрдХреНрд╕ рд╕рд╛рдордЧреНрд░реА рдХреЗ рд╕рд╛рде рд╕рд░рдгрд┐рдпреЛрдВ рдХреА рддреИрдпрд╛рд░реА рдХрд░ рд░рд╣реЗ рд╣реИрдВ ред рд╣рдордиреЗ рджреВрд╕рд░реЗ рдирдВрдмрд░ рдХреЛ рднреА рдЖрд░реНрдЧреНрд╕ рдПрд░реЗ рдореЗрдВ рд╕реЗрдЯ рдХрд┐рдпрд╛ рд╣реИ, рдЗрд╕реЗ рдЧрд┐рдирдиреЗ рдХреЗ рд▓рд┐рдП рд╕реЗрдЯ рдХрд░реЗрдВ ред

рдЗрд╕ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рд╣рдо рдХрд┐рд╕реА рднреА рдорд╛рди рдХреЛ рдЧрд┐рди рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ рдпрд╣ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рд╕реНрдкреНрд░рд╛рдЗрдЯреНрд╕ рдЙрддреНрдкрдиреНрди рдХрд░реЗрдЧрд╛, рд▓реЗрдХрд┐рди рдпрд╣ рдЙрдиреНрд╣реЗрдВ рд╕рд┐рд░реНрдл рдПрдХ рдбреНрд░реЙ рдХреЙрд▓ рдореЗрдВ рдкреНрд░рд╕реНрддреБрдд рдХрд░реЗрдЧрд╛ред


рдпрд╣рд╛рдВ 10,000 рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рд╕реНрдкреНрд░рд╛рдЗрдЯ рд╣реИрдВред

рдорд┐рдирд╕реНрдХреЗрд▓ рдФрд░ рдореИрдХреНрд╕рд╕реНрдХреЗрд▓ рдЕрдиреБрдХреНрд░рдорд┐рдд рдЪрд░ рдХреНрдпреЛрдВ рд╣реИрдВ? рдЬрдм рдореИрдВрдиреЗ 600,000 рд╕реНрдкреНрд░рд╛рдЗрдЯреНрд╕ рдХреЗ рд╕рд╛рде рдХреЛрдб рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛, рддреЛ рдореИрдВрдиреЗ рджреЗрдЦрд╛ рдХрд┐ рдЧрддрд┐ 60fps рд╕реЗ рдХрдо рд╣реЛ рдЧрдИ рд╣реИред рдпрджрд┐ рд╕реНрд░реЛрдд рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдПрдХ рдорд┐рд▓рд┐рдпрди рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИ, рддреЛ рдпрд╣ рдХреЛрдб рд╡рд┐рдлрд▓ рдХреНрдпреЛрдВ рд╣реЛрддрд╛ рд╣реИ?


рдпрд╣ 600,000 рд╕реНрдкреНрд░рд╛рдЗрдЯ рд╣реИред рдпрд╣ рдзреАрд░реЗ-рдзреАрд░реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред

рдореИрдВрдиреЗ рд╕реБрдЭрд╛рд╡ рджрд┐рдпрд╛ рдХрд┐ рд╢рд╛рдпрдж рдпрд╣ рд▓рд╛рд▓рдЪ рдХреЗ рдХрд╛рд░рдг рд╣реИред рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рдорд┐рдиреАрд╕реНрдХреЗрд▓ рдФрд░ рдореИрдХреНрд╕рд╕реЗрд▓ рдХреЛ рдХреНрд░рдордмрджреНрдз рд░реВрдк рд╕реЗ рдорд╛рдкрджрдгреНрдб рдмрдирд╛рдпрд╛ рдФрд░ 0.01 рдФрд░ 0.02 рдЬреИрд╕реА рдЫреЛрдЯреА рд╕рдВрдЦреНрдпрд╛рдПрдБ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХреАрдВред рдФрд░ рддрднреА рдореИрдВ 60fps (рд╕рдВрдкрд╛рджрдХ рдХреЗ рдкреНрд░реЛрдлрд╛рдЗрд▓рд░ рджреНрд╡рд╛рд░рд╛ рдЬрдЬ) рдкрд░ рдПрдХ рд▓рд╛рдЦ рд╕реНрдкреНрд░рд┐рдЯ рдХреЛ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдерд╛ред рд╢рд╛рдпрдж рдХреЛрдб рдЕрдзрд┐рдХ рд╕рдХреНрд╖рдо рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рд▓рд╛рдЦ рд╕реНрдкреНрд░рд╛рдЗрдЯ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХрд┐рд╕рдХреЛ рд╣реИ? рд╣рдорд╛рд░реЗ рдЦреЗрд▓ рдореЗрдВ, рдЗрд╕ рд╕рдВрдЦреНрдпрд╛ рдХреА рдПрдХ рдЪреМрдерд╛рдИ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред


рдПрдХ рд▓рд╛рдЦ рдЫреЛрдЯреЗ рд╕реНрдкреНрд░рд╛рдЗрдЯред

рдкреНрд░реЛрдлрд╛рдЗрд▓рд░


рдЗрд╕рд▓рд┐рдП, рдореИрдВ рдпрд╣ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ рдХрд┐ рдпрд╣ рдХреЛрдб рдЯреЗрд╕реНрдЯ рдмрд┐рд▓реНрдб рдореЗрдВ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред :


рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рд╕рдм рдХреБрдЫ рдмрд╣реБрдд рддреЗрдЬ рд╣реИред Graphics.DrawMeshInstancedIndirect () рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ 0 рдПрдордПрд╕ред рд╣рд╛рд▓рд╛рдБрдХрд┐ рдореИрдВ рдЗрддрдирд╛ рдирд┐рд╢реНрдЪрд┐рдд рдирд╣реАрдВ рд╣реВрдБ рдХрд┐ Gfx рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЪрд┐рдВрддрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред


рдЗрддрдирд╛ рд╢реАрдШреНрд░ рдирд╣реА


рд╣рд╛рд▓рд╛рдВрдХрд┐ рдкрд░рд┐рдгрд╛рдо рдкреНрд░рднрд╛рд╡рд╢рд╛рд▓реА рд╣реИ, рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЧреЗрдо рдореЗрдВ рдХреЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдПрдХ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд▓рд╛рдкрддрд╛ рдкрд╣рд▓реВ рд╕реНрдкреНрд░рд╛рдЗрдЯреНрд╕ рдХреА рдЫрдВрдЯрд╛рдИ рд╣реИред рдФрд░ рдпрд╣ рдЕрдзрд┐рдХрд╛рдВрд╢ CPU рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдХреЛ рд▓реЗ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдмрдврд╝рддреЗ рд╕реНрдкреНрд░рд╛рдЗрдЯреНрд╕ рдХреЗ рд╕рд╛рде, ComputeBuffers рдХреЛ рд╣рд░ рдлреНрд░реЗрдо рдореЗрдВ рдЕрдкрдбреЗрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЕрднреА рдмрд╣реБрдд рдХрд╛рдо рдмрд╛рдХреА рд╣реИред рдореБрдЭреЗ рдЙрдореНрдореАрдж рдирд╣реАрдВ рд╣реИ рдХрд┐ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдХрд╛рдордХрд╛рдЬреА рдврд╛рдВрдЪреЗ рдореЗрдВ рдПрдХ рдорд┐рд▓рд┐рдпрди рд╣рд╛рд╕рд┐рд▓ рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реЛрдЧрд╛, рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдореИрдВ 2 рдПрдордПрд╕ рд╕реЗ рдХрдо рдореЗрдВ 300,000 рдЬреИрд╕реА рдХреЛрдИ рдЪреАрдЬ рд╣рд╛рд╕рд┐рд▓ рдХрд░рддрд╛ рд╣реВрдВ, рддреЛ рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдлреА рдкрд░реНрдпрд╛рдкреНрдд рд╣реЛрдЧрд╛ред рдбреЙрдЯреНрд╕ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдЗрд╕рдХреЗ рд╕рд╛рде рдорджрдж рдХрд░реЗрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдпрд╣ рдПрдХ рдЕрдиреНрдп рд▓реЗрдЦ рдХреЗ рд▓рд┐рдП рдПрдХ рд╡рд┐рд╖рдп рд╣реИред

All Articles