๐ŸซงArt_006 Sine Grid [GPU Instancing]

BamgasiJMยท2026๋…„ 3์›” 13์ผ

Unity GenArt

๋ชฉ๋ก ๋ณด๊ธฐ
16/41
post-thumbnail

๐Ÿ“„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

    // ---------------------------------------------------
    // Unity ์ด๋ฒคํŠธ
    // ---------------------------------------------------

    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()
    {
        // โ–ถ๏ธ cubeMesh ์ž๋™ ์ƒ์„ฑ
        if (cubeMesh == null)
        {
            GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
            cubeMesh = temp.GetComponent<MeshFilter>().sharedMesh;
#if UNITY_EDITOR
            DestroyImmediate(temp);
#else
            Destroy(temp);
#endif
        }

        // โ–ถ๏ธ instanceMaterial ์ž๋™ ์ƒ์„ฑ (URP ์ „์šฉ)
        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);
        }
    }

    // ---------------------------------------------------
    // GPU ์ธ์Šคํ„ด์‹ฑ ๋“œ๋กœ์šฐ (PBR ์žฌ์งˆ ์ ์šฉ)
    // ---------------------------------------------------

    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 - PBR ์žฌ์งˆ ์„ค์ •
            MaterialPropertyBlock block = new MaterialPropertyBlock();
            
            // 1. Base Color (๋‹จ์ผ ์ƒ‰์ƒ - ๋ชจ๋“  ์ธ์Šคํ„ด์Šค ๋™์ผ)
            block.SetColor("_BaseColor", baseColor);
            
            // 2. Emission Color
            Color emissionColor = baseColor * emissionIntensity;
            block.SetColor("_EmissionColor", emissionColor);
            
            // 3. Metallic (URP/Lit ํ‘œ์ค€ ํ”„๋กœํผํ‹ฐ)
            block.SetFloat("_Metallic", metallic);
            
            // 4. Smoothness (URP/Lit ํ‘œ์ค€ ํ”„๋กœํผํ‹ฐ)
            block.SetFloat("_Smoothness", smoothness);
            
            // 5. Occlusion Strength
            block.SetFloat("_OcclusionStrength", occlusionStrength);

            // GPU ์ธ์Šคํ„ด์‹ฑ ๋“œ๋กœ์šฐ
            Graphics.DrawMeshInstanced(
                cubeMesh,
                0,
                instanceMaterial,
                batchMatrices,
                batch,
                block
            );

            index += batch;
            total -= batch;
        }
    }
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€