Renderiranje i animacija stotina tisuća blokova pomoću RenderMeshPrimitives
Public- 24 Sep, 2026

Slika se može razbiti na golemi niz blokova u boji pomoću Graphics.RenderMeshPrimitives, a zatim animirati valovima, sastavljanjem, odbijanjem i urušavanjem uz igriv broj sličica u sekundi. Pristup počinje tako da se izvorna tekstura pretvara u popis podataka o poziciji i boji svakog bloka. Zadaje se horizontalni broj blokova gridWidth, a okomiti broj izvodi se iz omjera širine i visine slike — tako se izvorni pikseli prorjeđuju u jednakim razmacima, pri čemu se potpuno prozirni i gotovo crni pikseli preskaču kao pozadina.
Svaki blok pohranjen je u strukturi PixelBlock s poljima float3 position i float4 color, koja točno ponavlja HLSL raspored. U Awake metoda texture.GetPixels32 čita izvornu sliku, a petlja for po koordinatama mreže preslikava svaku ćeliju mreže u izvorni piksel, pomičući pozicije tako da središte mreže bude u ishodištu. Bajtovi boje dijele se s 255, pretvarajući se u vrijednosti 0–1. Prvi naivni prolaz stvara po jedan GameObject po bloku sa zajedničkim meshom, materijalom i MaterialPropertyBlock, ali na otprilike 100 000 blokova sve se ruši na 6 FPS, jer svaki objekt dodaje naredbe iscrtavanja i režijske troškove komponenti.
Rješenje postaje Graphics.RenderMeshPrimitives — API koji je posebno napravljen za višestruko iscrtavanje jednog te istog mesha. Podaci instanci učitavaju se jednom u GraphicsBuffer (GraphicsBuffer.Target.Structured, sizeof(float) * 7 po elementu), zatim SetData(blocks) prenosi cijeli niz s CPU-a na GPU jednim pozivom. Argument stride mora odgovarati veličini HLSL strukture i treba ga ažurirati kad se polja promijene. Jedini poziv Graphics.RenderMeshPrimitives(renderParams, cubeMesh, 0, blocks.Length) iscrtava sve dok shader dohvaća poziciju i boju po SV_InstanceID. worldBounds mora biti dovoljno velik da obuhvati sve blokove: premalen skriva blokove, a prevelik poništava culling; međuspremnik bi trebalo osloboditi u OnDisablekako bi se izbjegla curenja.
Na otprilike 100 000 blokova verzija s GameObjectom narasla je na 54 525 poziva za iscrtavanje i oko 6 FPS, dok se verzija na RenderMeshPrimitives držala na 42 poziva za iscrtavanje za cijelu scenu i postizala 700–865 FPS. Pri povećanju od otprilike deset puta — na 1,04 milijuna blokova — pozivi za iscrtavanje ostali su na razini 42, a brzina kadrova držala se oko 435 FPS; brojke su preuzete iz prozora Statistics u Unityju. Budući da se pomak čita iz bafera na strani GPU-a, a CPU izdaje samo jednu naredbu za iscrtavanje, trošak ostaje nizak čak i kad broj blokova raste.
Animacija ide dvama putovima ovisno o tome je li potrebno stanje prethodnog kadra. Kretanje zadano samo vremenom u cijelosti se izvodi u vertex shaderu: val dodaje sin duž Z, koristeći _WaveAmplitude, _WaveFrequency i _WaveSpeed, a efekt sastavljanja interpolira raspršenu poziciju prema početnoj, upravljanu vrijednošću _Progress. Smjerovi raspršivanja uzimaju se iz GenerateHashedRandomFloat s ključem po ID-ju instance, pa su stoga deterministični i ne zahtijevaju pohranjeno stanje, a početni pragovi pomaknuti po visini čine da se slika podiže odozdo.
Za kretnje koje prenose brzinu ili položaj između okvira koristi se računalni shader. Efekt magneta pohranjuje trenutne položaje u međuspremnik za čitanje i pisanje i odbija blokove blizu pokazivača, zatim svaki okvir lerpa položaje prema njihovim ciljevima prije iscrtavanja ažuriranja. Gravitacijski urušaj svaki okvir integrira brzinu u međuspremnik BlockStateodskakujući pri slijetanju s _Restitution i uspavljujući se kada brzina padne ispod _SleepThreshold; _DeltaTime pri čemu se ograničava na 1/30 sekunde kako bi se izbjeglo eksplodiranje integracije, a položaji mirovanja unaprijed se računaju iz gustoće po stupcima.

