

GenArtBoilerplate โ โโโ [์ธํ๋ผ ์ฝ๋] ์์ ๋ถํ์ โ โโโ OnEnable โ EnsureResources โ Generate โ CacheStructureParams โ โโโ OnValidate โ QueueGenerate (๊ตฌ์กฐ ๋ณ๊ฒฝ) / Animate(0f) (์๊ฐ ๋ณ๊ฒฝ) โ โโโ QueueGenerate โ EditorApplication.delayCall โ Generate โ โโโ Update โ Animate(t) + RenderInstances โ โโโ EnsureResources: ๋ฉ์ยท๋จธํฐ๋ฆฌ์ผ ์๋ ์์ฑ โ โโโ RenderInstances: DrawMeshInstanced 1023๊ฐ ๋ฐฐ์น ๋ถํ โ โโโ DetectPipeline: URP/HDRP/Built-in ์๋ ๊ฐ์ง โ โโโ [์ํธ์ํฌ ๋ก์ง] ์ฌ๊ธฐ๋ง ์์ โโโ Generate() ๋ด๋ถ: ์ธ์คํด์ค ๋ฐฐ์น ๊ณต์ โโโ Animate() ๋ด๋ถ: ๋งค ํ๋ ์ ์์นยท์์ ๊ณ์ฐ ๊ณต์
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor; // EditorApplication.delayCall ์ฌ์ฉ์ ์ํด ์๋ํฐ ์ ์ฉ์ผ๋ก ์ํฌํธ
#endif
[ExecuteAlways] // ์๋ํฐ ๋ชจ๋(Play ์ )์์๋ Update ๋ฑ Unity ์ด๋ฒคํธ๊ฐ ํธ์ถ๋๊ฒ ํฉ๋๋ค
public class BoilerplateGPUInstancing : MonoBehaviour
{
// ============================================================
// ์ธ์คํํฐ ํ๋ผ๋ฏธํฐ
// Header ์ดํธ๋ฆฌ๋ทฐํธ๋ก ์ธ์คํํฐ๋ฅผ ์น์
๋ณ๋ก ๊ตฌ๋ถํฉ๋๋ค.
// ============================================================
// ----- ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ----------------------------------------
// ์ด ๊ฐ๋ค์ด ๋ฐ๋๋ฉด ์ค๋ธ์ ํธ๋ฅผ ์ ๋ถ ์ญ์ ํ๊ณ ๋ค์ ์์ฑํฉ๋๋ค.
// "๋ช ๊ฐ๋ฅผ ์ด๋์ ๋์ ๊ฒ์ธ๊ฐ"๋ฅผ ๊ฒฐ์ ํ๋ ํ๋ผ๋ฏธํฐ์
๋๋ค.
[Header("๊ตฌ์กฐ ์ค์ (๋ณ๊ฒฝ ์ ์ค๋ธ์ ํธ ์ฌ์์ฑ)")]
public int gridSize = 20; // ๊ฐ๋ก/์ธ๋ก ํ๋ธ ๊ฐ์
public float spacing = 0.8f; // ํ๋ธ ๊ฐ๊ฒฉ
public float cubeSize = 0.3f; // ํ๋ธ ํฌ๊ธฐ
public int colorSeed = 0; // ์์ ๋ฌด์์ ์๋ (๊ฐ์ ์๋ = ๊ฐ์ ์์ ๋ฐฐ์น)
// ----- ์ ๋๋ฉ์ด์
ํ๋ผ๋ฏธํฐ ----------------------------------
// ์ด ๊ฐ๋ค์ด ๋ฐ๋๋ฉด ์ฌ์์ฑ ์์ด ApplyAnimation(0f)๋ง ํธ์ถํฉ๋๋ค.
// "์ด๋ป๊ฒ ์์ง์ด๊ณ ์ด๋ป๊ฒ ๋ณด์ผ ๊ฒ์ธ๊ฐ"๋ฅผ ๊ฒฐ์ ํ๋ ํ๋ผ๋ฏธํฐ์
๋๋ค.
[Header("์ ๋๋ฉ์ด์
์ค์ (์ค์๊ฐ ๋ฐ์)")]
public float waveHeight = 2.0f; // ํ๋ ๋์ด
public float waveSpeed = 1.2f; // ํ๋ ์๋ (ํด์๋ก ๋น ๋ฆ)
public float waveFrequency = 0.8f; // ํ๋ ์ฃผํ์ (ํด์๋ก ์๊ฒ ํ๋)
// ----- ์์ ํ๋ผ๋ฏธํฐ ----------------------------------------
[Header("์์ ์ค์ (์ค์๊ฐ ๋ฐ์)")]
[Range(0f, 1f)] public float hueBase = 0.55f; // ์์ ์์ hue
[Range(0f, 1f)] public float hueRange = 0.40f; // ํ๋ธ ๊ฐ hue ๋ถํฌ ๋ฒ์
[Range(0f, 1f)] public float saturation = 0.80f; // ์ฑ๋ (0 = ํ๋ฐฑ, 1 = ํ ์ปฌ๋ฌ)
public float emissionIntensity = 1.0f; // ๋ฐ๊ด ๊ฐ๋
// ----- ์ฌ์ง ์ค์ --------------------------------------------
[Header("์ฌ์ง ์ค์ ")]
public Mesh instanceMesh; // GPU ๋๋ก์ฐ์ ์ฌ์ฉํ ๋ฉ์ (null์ด๋ฉด ์๋ ์์ฑ)
public Material instanceMaterial; // GPU ๋๋ก์ฐ์ ์ฌ์ฉํ ๋จธํฐ๋ฆฌ์ผ (null์ด๋ฉด ์๋ ์์ฑ)
// ============================================================
// ๋ด๋ถ ๋ฐ์ดํฐ ๊ตฌ์กฐ
// ============================================================
// ์ธ์คํด์ค ํ๋์ ์ ์ ๋ฐ์ดํฐ (Generate ์ ํ ๋ฒ๋ง ๊ณ์ฐ)
// ์ ๋๋ฉ์ด์
๋ฃจํ์์ ๋งค ํ๋ ์ ์ฐธ์กฐํฉ๋๋ค.
struct InstanceData
{
public Vector3 basePos; // ์์ฑ ์ ๊ฒฐ์ ๋ ๊ธฐ์ค ์์น (Y=0 ํ๋ฉด)
public float hue; // ์์ฑ ์ ๊ฒฐ์ ๋ ๊ณ ์ ์์ (์ ๋๋ฉ์ด์
์ค ๋ณํ์ง ์์)
public float brightness; // ์์ฑ ์ ๊ฒฐ์ ๋ ๊ธฐ๋ณธ ๋ฐ๊ธฐ
}
List<InstanceData> _instances = new List<InstanceData>(); // ์ ์ฒด ์ธ์คํด์ค ๋ฐ์ดํฐ ๋ชฉ๋ก
// GPU์ ๋๊ธธ ๋ฐฐ์ด (Animate์์ ๋งค ํ๋ ์ ๊ฐฑ์ , RenderInstances์์ GPU๋ก ์ ์ก)
Matrix4x4[] _matrices; // ๊ฐ ์ธ์คํด์ค์ ์์น/ํ์ /ํฌ๊ธฐ ํ๋ ฌ
Vector4[] _colors; // ๊ฐ ์ธ์คํด์ค์ albedo ์์
Vector4[] _emissions; // ๊ฐ ์ธ์คํด์ค์ emission ์์
// DrawMeshInstanced๋ ํ ๋ฒ ํธ์ถ์ ์ต๋ 1023๊ฐ๊น์ง๋ง ์ฒ๋ฆฌํฉ๋๋ค.
// 1023๊ฐ๋ฅผ ์ด๊ณผํ๋ฉด ์ฌ๋ฌ ๋ฒ ๋๋ ์ ํธ์ถํด์ผ ํฉ๋๋ค.
const int BATCH_SIZE = 1023;
bool _generated = false; // Generate ์๋ฃ ์ฌ๋ถ. false๋ฉด Update์์ ๋ ๋๋ง ๊ฑด๋๋
// ๋ ๋ ํ์ดํ๋ผ์ธ ์ข
๋ฅ (OnEnable์์ ์๋ ๊ฐ์ง)
enum RenderPipeline { BuiltIn, URP, HDRP }
RenderPipeline _pipeline;
// ============================================================
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ์บ์
// OnValidate์์ "๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ๊ฐ ์ค์ ๋ก ๋ฐ๋์๋์ง" ๋น๊ตํ๊ธฐ ์ํ ์ด์ ๊ฐ ์ ์ฅ์.
// ๋ฐ๋์ง ์์ ๊ฒฝ์ฐ ๋ถํ์ํ ์ฌ์์ฑ์ ๋ง์ต๋๋ค.
// ============================================================
int _cachedGridSize;
float _cachedSpacing;
float _cachedCubeSize;
int _cachedColorSeed;
#if UNITY_EDITOR
// QueueGenerate์ ์ค๋ณต ํธ์ถ ๋ฐฉ์ง ํ๋๊ทธ (์๋ํฐ ์ ์ฉ)
bool _generateQueued = false;
#endif
// ============================================================
// Unity ์ด๋ฒคํธ ๋ฉ์๋
// ============================================================
// OnEnable: ์ปดํฌ๋ํธ๊ฐ ํ์ฑํ๋ ๋ ํธ์ถ (์ฌ ๋ก๋, ์ปดํฌ๋ํธ ์ฒดํฌ ON ๋ฑ)
void OnEnable()
{
_pipeline = DetectPipeline(); // ํ์ฌ ํ๋ก์ ํธ์ ๋ ๋ ํ์ดํ๋ผ์ธ์ ๊ฐ์งํฉ๋๋ค
EnsureResources(); // ๋ฉ์์ ๋จธํฐ๋ฆฌ์ผ์ด ์์ผ๋ฉด ์๋ ์์ฑํฉ๋๋ค
Generate(); // ์ธ์คํด์ค ๋ฐ์ดํฐ๋ฅผ ์์ฑํฉ๋๋ค
CacheStructureParams(); // ํ์ฌ ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ๊ฐ์ ์บ์์ ์ ์ฅํฉ๋๋ค
}
// OnValidate: ์ธ์คํํฐ์์ ๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ํธ์ถ
// ์ฃผ์: ์ด ์ฝ๋ฐฑ ์์์ SetParent(), CreatePrimitive() ๋ฑ ์ฌ ๊ทธ๋ํ๋ฅผ
// ์ง์ ์์ ํ๋ฉด Unity ๊ฒฝ๊ณ ๊ฐ ๋ฐ์ํฉ๋๋ค.
// โ ์ฌ ๊ทธ๋ํ ๋ณ๊ฒฝ์ด ํ์ํ๋ฉด ๋ฐ๋์ QueueGenerate()๋ฅผ ์ฌ์ฉํฉ๋๋ค.
void OnValidate()
{
#if UNITY_EDITOR // OnValidate์ QueueGenerate ๋ก์ง์ ์๋ํฐ์์๋ง ํ์ํฉ๋๋ค
_pipeline = DetectPipeline();
EnsureResources();
if (StructureParamsChanged())
{
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ๊ฐ ๋ฐ๋์์ ๋
// โ ์ค๋ธ์ ํธ ์ฌ์์ฑ์ด ํ์ํ๋ฏ๋ก ๋ค์ ํ๋ ์์ผ๋ก Generate๋ฅผ ์์ฝํฉ๋๋ค
CacheStructureParams();
QueueGenerate();
}
else
{
// ์ ๋๋ฉ์ด์
/์์ ํ๋ผ๋ฏธํฐ๋ง ๋ฐ๋์์ ๋
// โ ์ฌ์์ฑ ์์ด t=0 ์ํ๋ก ์๊ฐ์ ๊ฒฐ๊ณผ๋ง ์ฆ์ ๊ฐฑ์ ํฉ๋๋ค
Animate(0f);
}
#endif
}
#if UNITY_EDITOR
// QueueGenerate: Generate()๋ฅผ ํ ํ๋ ์ ๋ค๋ก ๋ฏธ๋ฃจ์ด ์คํํฉ๋๋ค.
// OnValidate ์ปจํ
์คํธ์์ ์ง์ ์ฌ์ ์์ ํ๋ฉด Unity ๋ด๋ถ ์ง๋ ฌํ์ ์ถฉ๋ํ๊ธฐ ๋๋ฌธ์
// EditorApplication.delayCall๋ก ์์ ํ ํ์ด๋ฐ์ ์คํํฉ๋๋ค.
void QueueGenerate()
{
if (_generateQueued) return; // ์ด๋ฏธ ์์ฝ๋์ด ์์ผ๋ฉด ์ค๋ณต ์์ฝํ์ง ์์ต๋๋ค
_generateQueued = true;
EditorApplication.delayCall += () => // ๋ค์ ์๋ํฐ ๋ฃจํ ํ์ด๋ฐ์ ์คํ
{
_generateQueued = false;
if (this == null) return; // ์ค๋ธ์ ํธ๊ฐ ์ญ์ ๋ ๊ฒฝ์ฐ ์์ ํ๊ฒ ๊ฑด๋๋๋๋ค
Generate();
};
}
#endif
// Update: ๋งค ํ๋ ์ ํธ์ถ
void Update()
{
if (!_generated) return; // ์์ฑ์ด ์๋ฃ๋์ง ์์์ผ๋ฉด ๊ฑด๋๋๋๋ค
if (!Application.isPlaying)
{
// ์๋ํฐ Scene ๋ทฐ: ์ ๋๋ฉ์ด์
์์ด t=0 ์ ์ง ์ํ๋ก๋ง ๋ ๋๋งํฉ๋๋ค
Animate(0f);
RenderInstances();
return;
}
// Play ๋ชจ๋: ์๊ฐ ๊ธฐ๋ฐ์ผ๋ก ์ ๋๋ฉ์ด์
๊ฐฑ์ ํ ๋ ๋๋งํฉ๋๋ค
Animate(Time.time * waveSpeed);
RenderInstances();
}
// ============================================================
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ์บ์ ์ ํธ
// ============================================================
// ์ด์ ์ ์บ์ํด๋ ๊ฐ๊ณผ ํ์ฌ ๊ฐ์ ๋น๊ตํฉ๋๋ค.
// ํ๋๋ผ๋ ๋ค๋ฅด๋ฉด true๋ฅผ ๋ฐํํ์ฌ ์ฌ์์ฑ์ ํธ๋ฆฌ๊ฑฐํฉ๋๋ค.
bool StructureParamsChanged()
{
return gridSize != _cachedGridSize
|| spacing != _cachedSpacing
|| cubeSize != _cachedCubeSize
|| colorSeed != _cachedColorSeed;
}
// ํ์ฌ ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ๊ฐ์ ์บ์์ ์ ์ฅํฉ๋๋ค.
void CacheStructureParams()
{
_cachedGridSize = gridSize;
_cachedSpacing = spacing;
_cachedCubeSize = cubeSize;
_cachedColorSeed = colorSeed;
}
// ============================================================
// ๋ฆฌ์์ค ์ด๊ธฐํ
// ๋ฉ์์ ๋จธํฐ๋ฆฌ์ผ์ด ์ธ์คํํฐ์์ ํ ๋น๋์ง ์์ ๊ฒฝ์ฐ ์๋์ผ๋ก ์์ฑํฉ๋๋ค.
// ============================================================
void EnsureResources()
{
if (instanceMesh == null)
{
// ํ๋ธ ๋ฉ์๋ฅผ ์์ ์ค๋ธ์ ํธ์์ ์ถ์ถ ํ ์ฆ์ ์ญ์ ํฉ๋๋ค
GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
instanceMesh = temp.GetComponent<MeshFilter>().sharedMesh;
DestroyImmediate(temp); // ์ฌ์ ๋จ์ง ์๋๋ก ์ฆ์ ์ญ์
}
if (instanceMaterial == null)
{
// ํ๋ก์ ํธ์ ๋ ๋ ํ์ดํ๋ผ์ธ์ ๋ง๋ ์
ฐ์ด๋๋ฅผ ์๋์ผ๋ก ์ฐพ์ต๋๋ค
Shader shader =
Shader.Find("Universal Render Pipeline/Lit") ??
Shader.Find("HDRP/Lit") ??
Shader.Find("Standard");
instanceMaterial = new Material(shader);
instanceMaterial.enableInstancing = true; // GPU Instancing ๋ฐ๋์ ํ์ฑํ
instanceMaterial.EnableKeyword("_EMISSION");
instanceMaterial.globalIlluminationFlags =
MaterialGlobalIlluminationFlags.RealtimeEmissive;
Debug.Log("[GenArtBoilerplate] ๋จธํฐ๋ฆฌ์ผ ์๋ ์์ฑ ์๋ฃ");
}
}
// ============================================================
// ์์ฑ (GameObject๋ฅผ ๋ง๋ค์ง ์๊ณ ๋ฐ์ดํฐ ๋ฐฐ์ด๋ง ๊ตฌ์ฑํฉ๋๋ค)
// GPU Instancing์ GameObject๊ฐ ํ์ ์์ต๋๋ค.
// ์์น, ์์ ๋ฑ ๋ฐ์ดํฐ๋ง ๋ฐฐ์ด๋ก ์ค๋นํด์ GPU์ ๋๊น๋๋ค.
// ============================================================
void Generate()
{
_instances.Clear(); // ์ด์ ๋ฐ์ดํฐ๋ฅผ ์ด๊ธฐํํฉ๋๋ค
_generated = false; // ์์ฑ ์๋ฃ ํ๋๊ทธ๋ฅผ ๋ฆฌ์
ํฉ๋๋ค
// ---- [์ํธ์ํฌ ๋ก์ง] ์ฌ๊ธฐ๋ฅผ ์์ ํ์ธ์ ----
// ์์: ์ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๋ ๋ฐฐ์น
Random.InitState(colorSeed); // ์๋ ๊ณ ์ โ ๊ฐ์ ์๋๋ฉด ํญ์ ๋์ผํ ๋ฐฐ์น ์ฌํ
// ๊ทธ๋ฆฌ๋ ์ ์ฒด๋ฅผ ๋ก์ปฌ ์์ (0,0,0) ๊ธฐ์ค์ผ๋ก ์ค์ ์ ๋ ฌํฉ๋๋ค.
// offset ์์ด ๋ฐฐ์นํ๋ฉด (0,0,0)~(max,0,max) ๋ฒ์๊ฐ ๋์ด ํ์ชฝ์ผ๋ก ์น์ฐ์นฉ๋๋ค.
float offset = (gridSize - 1) * spacing * 0.5f;
for (int x = 0; x < gridSize; x++)
{
for (int z = 0; z < gridSize; z++)
{
_instances.Add(new InstanceData
{
// offset์ ๋นผ์ ๊ทธ๋ฆฌ๋ ์ค์ฌ์ด (0,0,0)์ด ๋๊ฒ ํฉ๋๋ค
basePos = new Vector3(x * spacing - offset, 0f, z * spacing - offset),
hue = (hueBase + Random.value * hueRange) % 1f, // ํ๋ธ๋ง๋ค ๊ณ ์ ์์
brightness = Random.Range(0.5f, 1.0f) // ํ๋ธ๋ง๋ค ๊ธฐ๋ณธ ๋ฐ๊ธฐ
});
}
}
// ---- [์ํธ์ํฌ ๋ก์ง ๋] ----
// GPU์ ๋๊ธธ ๋ฐฐ์ด์ ์ธ์คํด์ค ์์ ๋ง๊ฒ ์ฌ์ ํ ๋นํฉ๋๋ค
_matrices = new Matrix4x4[_instances.Count];
_colors = new Vector4[_instances.Count];
_emissions = new Vector4[_instances.Count];
_generated = true; // ์์ฑ ์๋ฃ โ Update์์ ๋ ๋๋ง์ ํ์ฉํฉ๋๋ค
Animate(0f); // ์์ฑ ์งํ t=0 ๊ธฐ์ค์ผ๋ก ์ด๊ธฐ ์ํ๋ฅผ ๊ณ์ฐํฉ๋๋ค
}
// ============================================================
// ์ ๋๋ฉ์ด์
(๋งค ํ๋ ์ ๋ฐฐ์ด ๊ฐ์ ๊ฐฑ์ ํฉ๋๋ค)
// t = 0 โ Scene ์๋ํฐ ์ ์ง ์ํ
// t = Time.time * waveSpeed โ Play ๋ชจ๋ ์ ๋๋ฉ์ด์
// ============================================================
void Animate(float t)
{
for (int i = 0; i < _instances.Count; i++)
{
InstanceData inst = _instances[i];
// ---- [์ํธ์ํฌ ๋ก์ง] ์ฌ๊ธฐ๋ฅผ ์์ ํ์ธ์ ----
// ์์: X+Z ๋ฐฉํฅ ์ฌ์ธ ํ๋
// x์ z ์ขํ์ ํฉ์ผ๋ก ์ฌ์ธํ๋ฅผ ๊ณ์ฐํฉ๋๋ค โ ๋๊ฐ์ ๋ฐฉํฅ ํ๋
float wave = Mathf.Sin(
(inst.basePos.x + inst.basePos.z) * waveFrequency + t
) * waveHeight;
// normalized: wave ๊ฐ์ 0~1 ๋ฒ์๋ก ๋ณํ (์์/ํฌ๊ธฐ ๊ณ์ฐ์ ํ์ฉ)
float normalized = (wave / waveHeight + 1f) * 0.5f;
// ์๋ ์ขํ๋ก ๋ณํ: ๋ถ๋ชจ Transform์ ์์น/ํ์ ์ด ๋ฐ์๋ฉ๋๋ค
Vector3 worldPos = transform.TransformPoint(
new Vector3(inst.basePos.x, wave, inst.basePos.z)
);
// TRS ํ๋ ฌ: Translation(์์น) ร Rotation(ํ์ ) ร Scale(ํฌ๊ธฐ)
// GPU๋ ์ด ํ๋ ฌ ํ๋๋ก ์ธ์คํด์ค์ ๋ชจ๋ ๋ณํ์ ์ฒ๋ฆฌํฉ๋๋ค
_matrices[i] = Matrix4x4.TRS(
worldPos,
transform.rotation,
Vector3.one * cubeSize
);
// ๊ณ ์ hue๋ ์ ์ง, ํ๋ ๋์ด์ ๋ฐ๋ผ ๋ฐ๊ธฐ๋ง ๋ณ์กฐํฉ๋๋ค
float animBrightness = inst.brightness * Mathf.Lerp(0.2f, 1.0f, normalized);
Color albedo = Color.HSVToRGB(inst.hue, saturation, animBrightness);
Color emission = Color.HSVToRGB(inst.hue, saturation, normalized)
* emissionIntensity;
// ---- [์ํธ์ํฌ ๋ก์ง ๋] ----
// ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ GPU ์ ์ก์ฉ ๋ฐฐ์ด์ ์ ์ฅํฉ๋๋ค
_colors[i] = new Vector4(albedo.r, albedo.g, albedo.b, 1f);
_emissions[i] = new Vector4(emission.r, emission.g, emission.b, 1f);
}
}
// ============================================================
// GPU ๋๋ก์ฐ
// Graphics.DrawMeshInstanced๋ก GameObject ์์ด ๋ฉ์๋ฅผ ๋ ๋๋งํฉ๋๋ค.
// ํ ๋ฒ์ ์ต๋ 1023๊ฐ ์ ํ์ด ์์ผ๋ฏ๋ก ๋ฐฐ์น๋ก ๋๋ ์ ํธ์ถํฉ๋๋ค.
// ============================================================
void RenderInstances()
{
if (instanceMesh == null || instanceMaterial == null) return;
int total = _instances.Count; // ๋จ์ ์ธ์คํด์ค ์
int index = 0; // ํ์ฌ ๋ฐฐ์น์ ์์ ์ธ๋ฑ์ค
while (total > 0)
{
int batch = Mathf.Min(BATCH_SIZE, total); // ์ด๋ฒ ๋ฐฐ์น ํฌ๊ธฐ (์ต๋ 1023)
// ๋ฐฐ์น ํฌ๊ธฐ๋งํผ ์์ ๋ฐฐ์ด์ ๋ง๋ค๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํฉ๋๋ค
Matrix4x4[] batchMatrices = new Matrix4x4[batch];
Vector4[] batchColors = new Vector4[batch];
Vector4[] batchEmissions = new Vector4[batch];
System.Array.Copy(_matrices, index, batchMatrices, 0, batch);
System.Array.Copy(_colors, index, batchColors, 0, batch);
System.Array.Copy(_emissions, index, batchEmissions, 0, batch);
// MaterialPropertyBlock: ์ธ์คํด์ค๋ณ ์์์ GPU์ ์ ๋ฌํฉ๋๋ค.
// ์ค์: ๋ฐฐ์น๋ง๋ค ๋ฐ๋์ new๋ก ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
// _block์ ์ฌ์ฌ์ฉํ๋ฉด ์ด์ ๋ฐฐ์น ๋ฐ์ดํฐ๊ฐ ๊ฐ์ญ์ ์ผ์ผํต๋๋ค.
MaterialPropertyBlock block = new MaterialPropertyBlock();
block.SetVectorArray("_BaseColor", batchColors);
block.SetVectorArray("_EmissionColor", batchEmissions);
// GPU์ ๋๋ก์ฐ ๋ช
๋ น์ ์ ๋ฌํฉ๋๋ค
// โ GameObject ์์ด ๋ฉ์๋ฅผ ์ฌ์ ๋ ๋๋งํฉ๋๋ค
Graphics.DrawMeshInstanced(
instanceMesh, // ๋ ๋๋งํ ๋ฉ์
0, // ์๋ธ๋ฉ์ ์ธ๋ฑ์ค (๋จ์ผ ๋ฉ์๋ ํญ์ 0)
instanceMaterial,
batchMatrices, // ์ธ์คํด์ค๋ณ TRS ํ๋ ฌ ๋ฐฐ์ด
batch, // ์ด๋ฒ ๋ฐฐ์น์ ์ธ์คํด์ค ์
block // ์ธ์คํด์ค๋ณ ๋จธํฐ๋ฆฌ์ผ ํ๋กํผํฐ
);
index += batch; // ๋ค์ ๋ฐฐ์น ์์ ์ธ๋ฑ์ค๋ก ์ด๋
total -= batch; // ๋จ์ ์ธ์คํด์ค ์ ์ฐจ๊ฐ
}
}
// ============================================================
// ๋ ๋ ํ์ดํ๋ผ์ธ ๊ฐ์ง
// ํ๋ก์ ํธ ์ค์ ์ ์ฝ์ด URP / HDRP / Built-in์ ์๋ ํ๋ณํฉ๋๋ค.
// ์
ฐ์ด๋ ํ๋กํผํฐ ์ด๋ฆ์ด ํ์ดํ๋ผ์ธ๋ง๋ค ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ํ์ํฉ๋๋ค.
// URP: _BaseColor, _EmissionColor
// HDRP: _BaseColor, _EmissiveColor
// Built-in: _Color, _EmissionColor
// ============================================================
RenderPipeline DetectPipeline()
{
var pipeline = UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline;
if (pipeline == null) return RenderPipeline.BuiltIn; // Built-in์ ์์
์ด null
string name = pipeline.GetType().Name;
if (name.Contains("Universal")) return RenderPipeline.URP;
if (name.Contains("HighDefinition") || name.Contains("HDRP")) return RenderPipeline.HDRP;
return RenderPipeline.BuiltIn;
}
}
์ด ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ "์ํธ์ํฌ ๋ก์ง๋ง ๊ต์ฒดํ๋ฉด ์๋ก์ด ์ํ์ด ์์ฑ" ๋๋ ๊ตฌ์กฐ๋ฅผ ๋ชฉํ๋ก ์ค๊ณ๋์์ต๋๋ค.
์ฝ๋๋ ๋ ๋ ์ด์ด๋ก ๋ช ํํ ๋๋ฉ๋๋ค.
| ๋ ์ด์ด | ์ญํ | ์์ ์ฌ๋ถ |
|---|---|---|
| ์ธํ๋ผ ์ฝ๋ | Unity ์ด๋ฒคํธ ์ฒ๋ฆฌ, GPU ๋๋ก์ฐ, ํ์ดํ๋ผ์ธ ๊ฐ์ง | ์์ ๋ถํ์ |
| ์ํธ์ํฌ ๋ก์ง | ์ค๋ธ์ ํธ ๋ฐฐ์น ๊ณต์, ์ ๋๋ฉ์ด์ ๊ณต์ | ์ด ๋ถ๋ถ๋ง ์์ |
[ ์ปดํฌ๋ํธ ํ์ฑํ ]
โ
โผ
OnEnable()
โโ DetectPipeline() โ ๋ ๋ ํ์ดํ๋ผ์ธ ๊ฐ์ง
โโ EnsureResources() โ ๋ฉ์ / ๋จธํฐ๋ฆฌ์ผ ์๋ ์์ฑ
โโ Generate() โ ์ธ์คํด์ค ๋ฐ์ดํฐ ๋ฐฐ์ด ๊ตฌ์ฑ
โ โโ Animate(0f) โ ์ด๊ธฐ ์ ์ง ์ํ ๊ณ์ฐ
โโ CacheStructureParams() โ ํ์ฌ ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ์ ์ฅ
โ
โผ (์ธ์คํํฐ ๊ฐ ๋ณ๊ฒฝ)
OnValidate()
โโ DetectPipeline
โโ EnsureResources
โโ StructureParamsChanged ?
โโ [ Yes ] โ CacheStructureParams โ QueueGenerate() โ delayCall โ Generate()
โโ [ No ] โ Animate(0f) (๊ตฌ์กฐ๋ณ๊ฒฝ์ ๋ฐ๋ฅธ ์ฌ์์ฑ ์์)
โ
โผ (๋งค ํ๋ ์)
Update()
โโ generated ํ์ธ
โโ [Editor ๋ชจ๋] โ Animate(0f) + RenderInstances()
โโ [Play ๋ชจ๋] โ Animate(Time.time * waveSpeed) + RenderInstances()
ํต์ฌ ํฌ์ธํธ: Generate()๋ ๋น์ฉ์ด ํฐ ์ฐ์ฐ์
๋๋ค. ๊ตฌ์กฐ๊ฐ ๋ฐ๋ ๋๋ง ํธ์ถํ๊ณ ,
๊ตฌ์กฐ๊ฐ ์ ๋ฐ๋๋ฉด ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฌ์ฉํ ์ฑ ์๊ฐ์ ์ธ ๋ณํ(waveHeight, hueBase ๋ฑ)๋ฅผ Animate()๋ง์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
OnEnable() โ ์ปดํฌ๋ํธ ์ง์
์ void OnEnable()
{
_pipeline = DetectPipeline();
EnsureResources();
Generate();
CacheStructureParams();
}
์ฌ ๋ก๋, ์ปดํฌ๋ํธ ํ์ฑํ, ์๋ํฐ์์ ์คํฌ๋ฆฝํธ ์ปดํ์ผ ์ฌ์ ์ฉ ๋ฑ
์ปดํฌ๋ํธ๊ฐ "์ผ์ง๋" ๋ชจ๋ ์ํฉ์์ ํธ์ถ๋ฉ๋๋ค.
ํธ์ถ ์์๊ฐ ์ค์ํฉ๋๋ค. Generate() ์ด์ ์ EnsureResources()๊ฐ ์คํ๋์ด์ผ
๋ฉ์์ ๋จธํฐ๋ฆฌ์ผ์ด ์ค๋น๋ ์ํ์์ ๋ฐ์ดํฐ ๋ฐฐ์ด์ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
DetectPipeline() โ ๋ ๋ ํ์ดํ๋ผ์ธ ์๋ ๊ฐ์งRenderPipeline DetectPipeline()
{
var pipeline = UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline;
if (pipeline == null) return RenderPipeline.BuiltIn;
string name = pipeline.GetType().Name;
if (name.Contains("Universal")) return RenderPipeline.URP;
if (name.Contains("HighDefinition")) return RenderPipeline.HDRP;
return RenderPipeline.BuiltIn;
}
URP, HDRP, Built-in์ ์ ฐ์ด๋ ํ๋กํผํฐ ์ด๋ฆ์ด ๋ค๋ฆ ๋๋ค.
| ํ์ดํ๋ผ์ธ | Albedo ํ๋กํผํฐ | Emission ํ๋กํผํฐ |
|---|---|---|
| URP | _BaseColor | _EmissionColor |
| HDRP | _BaseColor | _EmissiveColor |
| Built-in | _Color | _EmissionColor |
GraphicsSettings.currentRenderPipeline์ด null์ด๋ฉด Built-in์
๋๋ค.
Built-in์ ๋ณ๋์ Render Pipeline Asset์ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค.
EnsureResources() โ ๋ฉ์ยท๋จธํฐ๋ฆฌ์ผ ์๋ ์์ฑvoid EnsureResources()
{
if (instanceMesh == null)
{
GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
instanceMesh = temp.GetComponent<MeshFilter>().sharedMesh;
DestroyImmediate(temp);
}
if (instanceMaterial == null)
{
Shader shader =
Shader.Find("Universal Render Pipeline/Lit") ??
Shader.Find("HDRP/Lit") ??
Shader.Find("Standard");
instanceMaterial = new Material(shader);
instanceMaterial.enableInstancing = true; // ๋ฐ๋์ true
instanceMaterial.EnableKeyword("_EMISSION");
}
}
์ค๊ณ ์๋: ์ธ์คํํฐ์์ ์ง์ ํ ๋นํ์ง ์์๋ ์ปดํฌ๋ํธ๊ฐ ๋์ํ๋๋ก ํฉ๋๋ค.
GameObject.CreatePrimitive()๋ก ์์ ์ค๋ธ์ ํธ๋ฅผ ๋ง๋ค๊ณ sharedMesh๋ง ์ถ์ถํ ๋ค
์ฆ์ DestroyImmediate()๋ก ์ฌ์์ ์ ๊ฑฐํฉ๋๋ค. ์ฌ ๊ณ์ธต์ ์๋ฅ ์ค๋ธ์ ํธ๋ฅผ ๋จ๊ธฐ์ง ์์ต๋๋ค.
enableInstancing = true๋ GPU Instancing์ ํ์ ์กฐ๊ฑด์
๋๋ค.
์ด ํ๋๊ทธ๊ฐ ๊บผ์ ธ ์์ผ๋ฉด DrawMeshInstanced()๋ฅผ ํธ์ถํด๋ ์ธ์คํด์ฑ์ด ์ ์ฉ๋์ง ์์ต๋๋ค.
Generate() โ ์ธ์คํด์ค ๋ฐ์ดํฐ ๋ฐฐ์ด ๊ตฌ์ฑvoid Generate()
{
_instances.Clear();
_generated = false;
// [์ํธ์ํฌ ๋ก์ง] โ ๋ฐฐ์น ๊ณต์
Random.InitState(colorSeed);
float offset = (gridSize - 1) * spacing * 0.5f;
for (int x = 0; x < gridSize; x++)
for (int z = 0; z < gridSize; z++)
_instances.Add(new InstanceData { ... });
// GPU ์ ์ก ๋ฐฐ์ด ์ฌ์ ํ ๋น
_matrices = new Matrix4x4[_instances.Count];
_colors = new Vector4[_instances.Count];
_emissions = new Vector4[_instances.Count];
_generated = true;
Animate(0f);
}
Generate()๋ GameObject๋ฅผ ๋ง๋ค์ง ์์ต๋๋ค.
GPU Instancing์ ์ฌ ๊ณ์ธต(Hierarchy)์ ์ค๋ธ์ ํธ๋ฅผ ๋ฑ๋กํ์ง ์์ต๋๋ค.
๋์ InstanceData ๊ตฌ์กฐ์ฒด ๋ฐฐ์ด์ ๋ฉ๋ชจ๋ฆฌ์ ๊ตฌ์ฑํ๊ณ ,
์ดํ Animate()๊ฐ ์ด ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด GPU ์ ์ก์ฉ ๋ฐฐ์ด(_matrices, _colors)์ ์ฑ์๋๋ค.
_generated ํ๋๊ทธ๋ ์์ฑ์ด ์๋ฃ๋๊ธฐ ์ ์ Update()๊ฐ ๋ ๋๋ง์ ์๋ํ๋ ๊ฒ์ ๋ง๋
๊ฐ๋ ์กฐ๊ฑด์
๋๋ค. ์ด ํจํด์ ์ด๊ธฐํ ์์์ ์ํ NullReferenceException์ ์๋ฐฉํฉ๋๋ค.
Random.InitState(colorSeed)๋ก ์๋๋ฅผ ๊ณ ์ ํฉ๋๋ค. ๋์ผํ ์๋ ๊ฐ์ด๋ฉด
์๋ํฐ๋ฅผ ์ฌ์์ํด๋ ํญ์ ๋์ผํ ์์ ๋ฐฐ์น๊ฐ ์ฌํ๋ฉ๋๋ค.
OnValidate() + QueueGenerate() โ ์๋ํฐ ์ค์๊ฐ ๋ฐ์void OnValidate()
{
if (StructureParamsChanged()) {
CacheStructureParams();
QueueGenerate(); // โ ๋ค์ ํ๋ ์์ Generate() ์คํ
} else {
Animate(0f); // โ ์ฆ์ ์๊ฐ ๊ฐฑ์
}
}
void QueueGenerate()
{
if (_generateQueued) return;
_generateQueued = true;
EditorApplication.delayCall += () => {
_generateQueued = false;
if (this == null) return;
Generate();
};
}
OnValidate()๋ ์ธ์คํํฐ์์ ๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ํธ์ถ๋ฉ๋๋ค.
์ด ์ฝ๋ฐฑ ์์์ ์ง์ ์ฌ์ ์์ ํ๋ฉด Unity ๋ด๋ถ ์ง๋ ฌํ์ ์ถฉ๋ํฉ๋๋ค.
EditorApplication.delayCall์ ํ์ฌ ํ๋ ์์ ์ง๋ ฌํ ์ฌ์ดํด์ด ๋๋ ๋ค
์์ ํ ํ์ด๋ฐ์ ์ฝ๋ฐฑ์ ์คํํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
QueueGenerate()๋ EditorApplication.delayCall์ ์ฌ์ฉํด Generate()๋ฅผ ํ ํ๋ ์ ๋ค๋ก ๋ฏธ๋ฃน๋๋ค.
_generateQueued ํ๋๊ทธ๋ ์ธ์คํํฐ ์ฌ๋ผ์ด๋๋ฅผ ๋๋๊ทธํ ๋ OnValidate๊ฐ
์์ญ ๋ฒ ์ฐ์ ํธ์ถ๋์ด๋ Generate()๊ฐ ๋จ ํ ๋ฒ๋ง ์์ฝ๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
ํต์ฌ ์ค๊ณ ํฌ์ธํธ๋ ๋ ๊ฐ์ง์ ๋๋ค.
_generateQueued ํ๋๊ทธ.this == null ์ฒดํฌ.Animate(float t) โ ๋งค ํ๋ ์ ๋ฐฐ์ด ๊ฐฑ์ void Animate(float t)
{
for (int i = 0; i < _instances.Count; i++)
{
InstanceData inst = _instances[i];
// [์ํธ์ํฌ ๋ก์ง] โ ์์นยท์์ ๊ณต์
float wave = Mathf.Sin(
(inst.basePos.x + inst.basePos.z) * waveFrequency + t
) * waveHeight;
float normalized = (wave / waveHeight + 1f) * 0.5f;
Vector3 worldPos = transform.TransformPoint(
new Vector3(inst.basePos.x, wave, inst.basePos.z)
);
_matrices[i] = Matrix4x4.TRS(worldPos, transform.rotation, Vector3.one * cubeSize);
_colors[i] = (Vector4)Color.HSVToRGB(inst.hue, saturation, animBrightness);
_emissions[i] = (Vector4)(Color.HSVToRGB(inst.hue, saturation, normalized) * emissionIntensity);
}
}
t = 0f์ด๋ฉด ์ ์ง ์ํ(์๋ํฐ ์ฌ ๋ทฐ),
t = Time.time * waveSpeed์ด๋ฉด Play ๋ชจ๋ ์ ๋๋ฉ์ด์
์
๋๋ค.
Matrix4x4.TRS() ๋ Translation ร Rotation ร Scale ํ๋ ฌ์ ํ ๋ฒ์ ์์ฑํฉ๋๋ค.
GPU๋ ์ด ํ๋ ฌ ํ๋๋ก ์ธ์คํด์ค์ ๋ชจ๋ ๊ณต๊ฐ ๋ณํ์ ์ฒ๋ฆฌํฉ๋๋ค.
transform.TransformPoint() ๋ ๋ก์ปฌ ์ขํ๋ฅผ ์๋ ์ขํ๋ก ๋ณํํฉ๋๋ค.
์ปดํฌ๋ํธ๊ฐ ๋ถ์ด ์๋ GameObject์ ์์น/ํ์ ์ด ์๋์ผ๋ก ๋ฐ์๋ฉ๋๋ค.
normalized ๋ณ์๋ ์ฌ์ธํ์ -1 ~ +1 ๋ฒ์๋ฅผ 0 ~ 1๋ก ์ ๊ทํํ ๊ฐ์
๋๋ค.
์ด ๊ฐ์ ์์์ ๋ฐ๊ธฐ(Value)์ ์ฐ๊ฒฐํ๋ฉด ํ๋ ๋๋ฎ์ด์ ์์ ๋ฐ๊ธฐ๊ฐ ์ฐ๋๋ฉ๋๋ค.
RenderInstances() โ GPU ๋๋ก์ฐ ๋ช
๋ น ๋ฐํvoid RenderInstances()
{
int total = _instances.Count;
int index = 0;
while (total > 0)
{
int batch = Mathf.Min(BATCH_SIZE, total); // BATCH_SIZE = 1023
Matrix4x4[] batchMatrices = new Matrix4x4[batch];
Vector4[] batchColors = new Vector4[batch];
Vector4[] batchEmissions = new Vector4[batch];
System.Array.Copy(_matrices, index, batchMatrices, 0, batch);
System.Array.Copy(_colors, index, batchColors, 0, batch);
System.Array.Copy(_emissions, index, batchEmissions, 0, batch);
MaterialPropertyBlock block = new MaterialPropertyBlock();
block.SetVectorArray("_BaseColor", batchColors);
block.SetVectorArray("_EmissionColor", batchEmissions);
Graphics.DrawMeshInstanced(
instanceMesh, 0, instanceMaterial,
batchMatrices, batch, block
);
index += batch;
total -= batch;
}
}
Graphics.DrawMeshInstanced()๋ GameObject ์์ด ๋ฉ์๋ฅผ ๋ ๋๋งํ๋ ํต์ฌ API์
๋๋ค.
์ด ํจ์ ํ๋๋ก batch๊ฐ์ ๋ฉ์๋ฅผ GPU์ ๋จ ํ ๋ฒ์ ๋๋ก์ฐ ์ฝ๋ก ๋ ๋๋งํฉ๋๋ค.
1023 ์ ํ์ ์ด์ :
DrawMeshInstanced()๋ ๋ด๋ถ์ ์ผ๋ก GPU ์์ ๋ฒํผ(Constant Buffer)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ๋ซํผ ๊ณตํต ์ต์ ๋ณด์ฅ ์์ ๋ฒํผ ํฌ๊ธฐ(D3D11 ๊ธฐ์ค 4096 float4 ์ฌ๋กฏ)์
Unity ๋ด๋ถ ์ค๋ฒํค๋๋ฅผ ๊ณ ๋ คํ ์ค์ง์ ์ํ์ด 1023์
๋๋ค.
MaterialPropertyBlock์ ๋ฐฐ์น๋ง๋ค new๋ก ์์ฑํ๋ ์ด์ :
๊ฐ์ block ์ธ์คํด์ค๋ฅผ SetVectorArray()๋ก ๋ฎ์ด์ฐ๋ฉด
์ด์ ๋ฐฐ์น์ ๋ํ ๋๋ก์ฐ ์ฝ์ด ์์ง GPU ํ์ ์๋ ์์ ์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ์ ์์ต๋๋ค.
๋ฐฐ์น๋ง๋ค ์ ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๊ฒ์ด ์์ ํฉ๋๋ค.
Generate()
โ
โโโ InstanceData[] _instances (์ ์ ๋ฐ์ดํฐ โ ๋ฐฐ์น ํ ๋ณํ์ง ์์)
โ โโโ basePos : Vector3 ๊ธฐ์ค ์์น (Y=0 ํ๋ฉด)
โ โโโ hue : float ๊ณ ์ ์์
โ โโโ brightness : float ๊ธฐ๋ณธ ๋ฐ๊ธฐ
โ
Animate(t)
โ
โโโ _matrices[] : Matrix4x4[] โ TRS ํ๋ ฌ (์์น + ํ์ + ํฌ๊ธฐ)
โโโ _colors[] : Vector4[] โ Albedo ์์
โโโ _emissions[] : Vector4[] โ Emission ์์
โ
RenderInstances()
โ
โโโ Graphics.DrawMeshInstanced() GPU โ ํ๋ฉด
โโโ MaterialPropertyBlock ์ธ์คํด์ค๋ณ ์์ ์ฃผ์
_instances๋ Generate()์์ ํ ๋ฒ๋ง ๊ตฌ์ฑ๋ฉ๋๋ค.
_matrices, _colors, _emissions๋ Animate()๊ฐ ๋งค ํ๋ ์ ๋ฎ์ด์๋๋ค.
RenderInstances()๋ ์์ฑ๋ ๋ฐฐ์ด์ ๊ทธ๋๋ก GPU์ ์ ๋ฌํฉ๋๋ค.
[ExecuteAlways]
public class BoilerplateGPUInstancing : MonoBehaviour
๊ธฐ๋ณธ์ ์ผ๋ก Update(), OnEnable() ๋ฑ Unity ์ด๋ฒคํธ๋ Play ๋ชจ๋์์๋ง ํธ์ถ๋ฉ๋๋ค.
[ExecuteAlways]๋ฅผ ๋ถ์ด๋ฉด ์๋ํฐ ์ฌ ๋ทฐ์์๋ ์ด๋ฒคํธ๊ฐ ํธ์ถ๋ฉ๋๋ค.
์ด ๋ณด์ผ๋ฌํ๋ ์ดํธ์์๋ ์ด ์์ฑ ๋๋ถ์:
Update() ์์์ Application.isPlaying์ผ๋ก ๋ถ๊ธฐํ์ฌ
์๋ํฐ์์๋ Animate(0f) (์ ์ง),
Play ๋ชจ๋์์๋ Animate(Time.time * waveSpeed) (์ ๋๋ฉ์ด์
)์ ์ ํํฉ๋๋ค.
๋๋ง์ GPU Instancing ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ฅผ ์ฒ์๋ถํฐ ์์ฑํ ๋ ๋ฐ๋์ ํฌํจํด์ผ ํ ํญ๋ชฉ์ ๋๋ค.
[ExecuteAlways] ์ดํธ๋ฆฌ๋ทฐํธstruct InstanceData โ ์ ์ ๋ฐ์ดํฐ ์ ์ฅ ๊ตฌ์กฐ์ฒดList<InstanceData> _instances โ ์ธ์คํด์ค ๋ชฉ๋กMatrix4x4[] _matrices, Vector4[] _colors โ GPU ์ ์ก ๋ฐฐ์ดconst int BATCH_SIZE = 1023 โ ๋ฐฐ์น ์ํbool _generated โ ์์ฑ ์๋ฃ ๊ฐ๋ ํ๋๊ทธOnEnable() โ EnsureResources() โ Generate() โ CacheStructureParams()OnValidate() โ ๊ตฌ์กฐ ๋ณ๊ฒฝ ์ฌ๋ถ ๋ถ๊ธฐ โ QueueGenerate() or Animate(0f)QueueGenerate() โ EditorApplication.delayCall ํจํดUpdate() โ isPlaying ๋ถ๊ธฐ โ Animate(t) + RenderInstances()EnsureResources() โ ๋ฉ์ยท๋จธํฐ๋ฆฌ์ผ null ์ฒดํฌ + ์๋ ์์ฑGenerate() โ _instances ๊ตฌ์ฑ + ๋ฐฐ์ด ํ ๋น + Animate(0f) ํธ์ถAnimate(float t) โ _matrices, _colors ๊ฐฑ์ ๋ฃจํRenderInstances() โ 1023 ๋ฐฐ์น ๋ถํ while ๋ฃจํ + DrawMeshInstanced()| ํจ์ | ๊ต์ฒด ๋ด์ฉ |
|---|---|
Generate() | ์ธ์คํด์ค๋ฅผ ๋ช ๊ฐ, ์ด๋ค ํจํด์ผ๋ก ๋ฐฐ์นํ ๊ฒ์ธ๊ฐ |
Animate(t) | ๋งค ํ๋ ์ ์์นยท์์์ ์ด๋ค ์์์ผ๋ก ๊ณ์ฐํ ๊ฒ์ธ๊ฐ |
์ด ๋ ํจ์์ ์์๋ง ๋ฐ๊พธ๋ฉด ํ๋, ๋์ , ๋
ธ์ด์ฆ ํ๋, ์ดํธ๋ํฐ ๋ฑ
์ด๋ค ์ํธ์ํฌ ํจํด์ผ๋ก๋ ํ์ฅํ ์ ์์ต๋๋ค.
| API | ์ค๋ช |
|---|---|
Graphics.DrawMeshInstanced() | GameObject ์์ด GPU ์ธ์คํด์ฑ์ผ๋ก ๋ฉ์ ๋ ๋๋ง |
MaterialPropertyBlock | ๋จธํฐ๋ฆฌ์ผ ๋ณต์ฌ ์์ด ์ธ์คํด์ค๋ณ ํ๋กํผํฐ ์ค์ |
Matrix4x4.TRS() | TranslationยทRotationยทScale ๋ณตํฉ ํ๋ ฌ ์์ฑ |
transform.TransformPoint() | ๋ก์ปฌ ์ขํ โ ์๋ ์ขํ ๋ณํ |
EditorApplication.delayCall | ์๋ํฐ ๋ฃจํ ๋ค์ ํฑ์ ์ฝ๋ฐฑ ์์ฝ |
GraphicsSettings.currentRenderPipeline | ํ์ฌ ๋ ๋ ํ์ดํ๋ผ์ธ ์์ ๋ฐํ |
Random.InitState(seed) | Unity ๋ด์ฅ ๋์ ์๋ ์ด๊ธฐํ |