
๐Art_006_Sine_Grid_GPU.cs
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteAlways]
public class Art_SineGrid_GPU : MonoBehaviour
{
[Header("๊ทธ๋ฆฌ๋ ๊ตฌ์กฐ (๋ณ๊ฒฝ ์ ์ฌ๊ณ์ฐ)")]
public int gridSize = 150;
public float spacing = 0.3f;
public float cubeSize = 0.25f;
public int colorSeed = 0;
[Header("ํ๋ ์ค์ (์ค์๊ฐ ๋ฐ์)")]
public float waveHeight = 0.4f;
public float waveSpeed = 1.5f;
public float waveFrequency = 0.8f;
[Header("์์ ์ค์ (์ค์๊ฐ ๋ฐ์)")]
[ColorUsage(false)] public Color baseColor = Color.white;
public float emissionIntensity = 0.4f;
[Header("PBR ์ฌ์ง ์ค์ (์ค์๊ฐ ๋ฐ์)")]
[Range(0f, 1f)] public float metallic = 0.0f;
[Range(0f, 1f)] public float smoothness = 0.5f;
[Range(0f, 1f)] public float occlusionStrength = 1.0f;
[Header("์ฌ์ง ์ค์ ")]
public Mesh cubeMesh;
public Material instanceMaterial;
private int _cachedGridSize;
private float _cachedSpacing;
private float _cachedCubeSize;
private int _cachedColorSeed;
struct InstanceData
{
public Vector3 basePos;
}
List<InstanceData> _instances = new List<InstanceData>();
Matrix4x4[] _matrices;
const int BATCH_SIZE = 1023;
bool _generated = false;
#if UNITY_EDITOR
bool _generateQueued = false;
#endif
void OnEnable()
{
EnsureResources();
Generate();
CacheStructureParams();
}
void OnValidate()
{
#if UNITY_EDITOR
EnsureResources();
if (StructureParamsChanged())
{
CacheStructureParams();
QueueGenerate();
}
#endif
}
#if UNITY_EDITOR
void QueueGenerate()
{
if (_generateQueued) return;
_generateQueued = true;
EditorApplication.delayCall += () =>
{
_generateQueued = false;
if (this == null) return;
Generate();
};
}
#endif
void Update()
{
if (!_generated) return;
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Animate(0f);
RenderInstances();
return;
}
#endif
Animate(Time.time * waveSpeed);
RenderInstances();
}
bool StructureParamsChanged()
{
return gridSize != _cachedGridSize
|| spacing != _cachedSpacing
|| cubeSize != _cachedCubeSize
|| colorSeed != _cachedColorSeed;
}
void CacheStructureParams()
{
_cachedGridSize = gridSize;
_cachedSpacing = spacing;
_cachedCubeSize = cubeSize;
_cachedColorSeed = colorSeed;
}
void EnsureResources()
{
if (cubeMesh == null)
{
GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
cubeMesh = temp.GetComponent<MeshFilter>().sharedMesh;
#if UNITY_EDITOR
DestroyImmediate(temp);
#else
Destroy(temp);
#endif
}
if (instanceMaterial == null)
{
Shader urpShader = Shader.Find("Universal Render Pipeline/Lit");
if (urpShader == null)
{
Debug.LogWarning("[Art_SineGrid_GPU] URP ์
ฐ์ด๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. Inspector ์์ instanceMaterial ์ ์ง์ ํ ๋นํด์ฃผ์ธ์.");
return;
}
instanceMaterial = new Material(urpShader);
instanceMaterial.enableInstancing = true;
instanceMaterial.EnableKeyword("_EMISSION");
instanceMaterial.globalIlluminationFlags =
MaterialGlobalIlluminationFlags.RealtimeEmissive;
Debug.Log("[Art_SineGrid_GPU] ์ธ์คํด์ค ๋จธํฐ๋ฆฌ์ผ ์๋ ์์ฑ (URP/Lit)");
}
}
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
{
basePos = new Vector3(x * spacing - offset, 0f, z * spacing - offset)
});
}
}
_matrices = new Matrix4x4[_instances.Count];
_generated = true;
Animate(0f);
}
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;
Vector3 pos = transform.TransformPoint(
new Vector3(inst.basePos.x, wave, inst.basePos.z)
);
_matrices[i] = Matrix4x4.TRS(pos, transform.rotation, Vector3.one * cubeSize);
}
}
void RenderInstances()
{
if (cubeMesh == null || instanceMaterial == null || !_generated) return;
int total = _instances.Count;
int index = 0;
while (total > 0)
{
int batch = Mathf.Min(BATCH_SIZE, total);
Matrix4x4[] batchMatrices = new Matrix4x4[batch];
System.Array.Copy(_matrices, index, batchMatrices, 0, batch);
MaterialPropertyBlock block = new MaterialPropertyBlock();
block.SetColor("_BaseColor", baseColor);
Color emissionColor = baseColor * emissionIntensity;
block.SetColor("_EmissionColor", emissionColor);
block.SetFloat("_Metallic", metallic);
block.SetFloat("_Smoothness", smoothness);
block.SetFloat("_OcclusionStrength", occlusionStrength);
Graphics.DrawMeshInstanced(
cubeMesh,
0,
instanceMaterial,
batchMatrices,
batch,
block
);
index += batch;
total -= batch;
}
}
}