๐ŸซงArt_001 Spiral Cube Generator

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

Unity GenArt

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

๐Ÿ“’ Art_001_Spiral_Cube_Generator.cs

using UnityEngine;
using System.Collections.Generic;

#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteAlways]
public class SpiralCubeGenerator : MonoBehaviour
{
    [Header("๋‚˜์„  ๊ตฌ์กฐ (๋ณ€๊ฒฝ ์‹œ ์˜ค๋ธŒ์ ํŠธ ์žฌ์ƒ์„ฑ)")]
    public int cubeCount = 2000;
    public float angleStep = 0.5f;   // ํ๋ธŒ ๊ฐ„ ๊ฐ๋„ ๊ฐ„๊ฒฉ (๋ผ๋””์•ˆ)
    public float radiusStep = 0.04f;  // ํ๋ธŒ ๊ฐ„ ๋ฐ˜์ง€๋ฆ„ ์ฆ๊ฐ€๋Ÿ‰
    public float depthStep = 0.1f;   // ํ๋ธŒ ๊ฐ„ Z์ถ• ์ „์ง„๋Ÿ‰

    [Header("์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ •")]
    public float rotationSpeed = 0.3f;  // ๋‚˜์„  ํšŒ์ „ ์†๋„
    public float waveAmplitude = 0.2f;  // Z ๋ฐฉํ–ฅ ํŒŒ๋™ ์ง„ํญ
    public float waveFrequency = 2.0f;  // ํŒŒ๋™ ์ฃผํŒŒ์ˆ˜

    [Header("์ƒ‰์ƒ")]
    [Range(0f, 1f)] public float hueBase = 0.46f;
    [Range(0f, 1f)] public float hueRange = 0.24f;
    [Range(0f, 1f)] public float saturation = 0.4f;
    [Range(0f, 1f)] public float brightness = 1.0f;

    // ---------------------------------------------------
    // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์บ์‹œ (OnValidate ๋น„๊ต์šฉ)
    // ---------------------------------------------------
    private int   _cachedCubeCount;
    private float _cachedAngleStep;
    private float _cachedRadiusStep;
    private float _cachedDepthStep;

    // ---------------------------------------------------
    // ํ๋ธŒ ๋ฐ์ดํ„ฐ
    // ---------------------------------------------------
    struct CubeData
    {
        public Transform tr;
        public Material  mat;
        public float     baseAngle;      // ์ƒ์„ฑ ์‹œ ๊ฐ๋„
        public float     radius;         // ๋‚˜์„  ๋ฐ˜์ง€๋ฆ„
        public float     baseDepth;      // Z ๊ธฐ๋ณธ๊ฐ’
        public float     normalized;     // 0~1 (์ƒ‰์ƒ์šฉ)
    }

    List<CubeData> _cubes = new List<CubeData>();

#if UNITY_EDITOR
    bool _generateQueued = false;
#endif

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

    void OnEnable()
    {
        Generate();
        CacheStructureParams();
    }

    void OnValidate()
    {
#if UNITY_EDITOR
        if (StructureParamsChanged())
        {
            CacheStructureParams();
            QueueGenerate();
        }
        else
        {
            // ์ƒ‰์ƒยท์• ๋‹ˆ๋ฉ”์ด์…˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ฐ”๋€ ๊ฒฝ์šฐ โ†’ ์ฆ‰์‹œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฐฑ์‹ 
            ApplyAnimation(0f);
        }
#endif
    }

#if UNITY_EDITOR
    void QueueGenerate()
    {
        if (_generateQueued) return;

        _generateQueued = true;
        EditorApplication.delayCall += () =>
        {
            _generateQueued = false;
            if (this == null) return;
            Generate();
        };
    }
#endif

    void Update()
    {
        // ์—๋””ํ„ฐ Scene ๋ทฐ์—์„œ๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ •์ง€
#if UNITY_EDITOR
        if (!Application.isPlaying) return;
#endif
        ApplyAnimation(Time.time);
    }

    // ---------------------------------------------------
    // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€๊ฒฝ ๊ฐ์ง€
    // ---------------------------------------------------

    bool StructureParamsChanged()
    {
        return cubeCount    != _cachedCubeCount
            || angleStep    != _cachedAngleStep
            || radiusStep   != _cachedRadiusStep
            || depthStep    != _cachedDepthStep;
    }

    void CacheStructureParams()
    {
        _cachedCubeCount  = cubeCount;
        _cachedAngleStep  = angleStep;
        _cachedRadiusStep = radiusStep;
        _cachedDepthStep  = depthStep;
    }

    // ---------------------------------------------------
    // ์ƒ์„ฑ
    // ---------------------------------------------------

    void Generate()
    {
        Clear();

        for (int i = 0; i < cubeCount; i++)
        {
            float t     = (cubeCount > 1) ? (float)i / (cubeCount - 1) : 0f;
            float angle = i * angleStep;
            float r     = i * radiusStep;
            float z     = i * depthStep;

            // ๋‚˜์„  ๊ณต์‹: XY ํ‰๋ฉด์—์„œ ํšŒ์ „ํ•˜๋ฉฐ Z ๋ฐฉํ–ฅ์œผ๋กœ ์ „์ง„
            Vector3 pos = new Vector3(
                Mathf.Cos(angle) * r,
                Mathf.Sin(angle) * r,
                z
            );

            SpawnCube(pos, angle, r, z, t);
        }

        ApplyAnimation(0f);
    }

    void SpawnCube(Vector3 pos, float angle, float radius, float depth, float normalized)
    {
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
        go.transform.SetParent(transform);
        go.transform.localPosition = pos;

        Material mat = new Material(Shader.Find("Universal Render Pipeline/Lit"));
        mat.EnableKeyword("_EMISSION");
        mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;

        go.GetComponent<Renderer>().material = mat;

        _cubes.Add(new CubeData
        {
            tr         = go.transform,
            mat        = mat,
            baseAngle  = angle,
            radius     = radius,
            baseDepth  = depth,
            normalized = normalized
        });
    }

    void Clear()
    {
        _cubes.Clear();
        while (transform.childCount > 0)
            DestroyImmediate(transform.GetChild(0).gameObject);
    }

    // ---------------------------------------------------
    // ์• ๋‹ˆ๋ฉ”์ด์…˜
    // ---------------------------------------------------

    void ApplyAnimation(float t)
    {
        for (int i = 0; i < _cubes.Count; i++)
        {
            CubeData c = _cubes[i];
            if (c.tr == null || c.mat == null) continue;

            // ๋‚˜์„  ์ „์ฒด๋ฅผ Z์ถ• ์ค‘์‹ฌ์œผ๋กœ ์ฒœ์ฒœํžˆ ํšŒ์ „
            float animAngle = c.baseAngle + t * rotationSpeed;

            // Z์ถ• ํŒŒ๋™: ๊ฐ ํ๋ธŒ๊ฐ€ ๋ฌผ๊ฒฐ์น˜๋“ฏ ์•ž๋’ค๋กœ ์›€์ง์ž„
            float wave = Mathf.Sin(c.normalized * waveFrequency * Mathf.PI * 2f - t) * waveAmplitude;

            c.tr.localPosition = new Vector3(
                Mathf.Cos(animAngle) * c.radius,
                Mathf.Sin(animAngle) * c.radius,
                c.baseDepth + wave
            );

            // ํ๋ธŒ ์ž์ฒด๋„ ๋‚˜์„  ๋ฐฉํ–ฅ์œผ๋กœ ์ž์ „
            c.tr.localRotation = Quaternion.Euler(0f, 0f, animAngle * Mathf.Rad2Deg);

            // ์ƒ‰์ƒ
            float hue     = (hueBase + c.normalized * hueRange) % 1f;
            Color albedo   = Color.HSVToRGB(hue, saturation, brightness);
            Color emission = Color.HSVToRGB(hue, 1f, c.normalized) * 0.5f;

            c.mat.SetColor("_BaseColor",    albedo);
            c.mat.SetColor("_EmissionColor", emission);
        }
    }
}

๐Ÿ“„Art_001_Spiral_Cube_Generator_Ver2.cs

using UnityEngine;
using System.Collections.Generic;

#if UNITY_EDITOR
using UnityEditor;
#endif

// ============================================================
// SpiralCubeGenerator
// ๋‚˜์„ ํ˜•์œผ๋กœ ํ๋ธŒ๋ฅผ ๋ฐฐ์น˜ํ•˜๊ณ  ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•˜๋Š” ์ œ๋„ˆ๋ ˆ์ดํ‹ฐ๋ธŒ ์•„ํŠธ ์ปดํฌ๋„ŒํŠธ.
// [ExecuteAlways] ๋กœ ์—๋””ํ„ฐ Scene ๋ทฐ์—์„œ๋„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
// ============================================================
[ExecuteAlways]
public class SpiralCubeGeneratorVer2 : MonoBehaviour
{
	// -----------------------------------------------------------
	// Inspector ํŒŒ๋ผ๋ฏธํ„ฐ
	// -----------------------------------------------------------

	[Header("๋‚˜์„  ๊ตฌ์กฐ (๋ณ€๊ฒฝ ์‹œ ์˜ค๋ธŒ์ ํŠธ ์žฌ์ƒ์„ฑ)")]
	public int cubeCount = 2000;
	public float angleStep = 0.5f;   // ํ๋ธŒ ๊ฐ„ ๊ฐ๋„ ๊ฐ„๊ฒฉ (๋ผ๋””์•ˆ)
	public float radiusStep = 0.04f;  // ํ๋ธŒ ๊ฐ„ ๋ฐ˜์ง€๋ฆ„ ์ฆ๊ฐ€๋Ÿ‰
	public float depthStep = 0.1f;   // ํ๋ธŒ ๊ฐ„ Z์ถ• ์ „์ง„๋Ÿ‰

	[Header("ํ๋ธŒ ํฌ๊ธฐ")]
	public float cubeScale = 0.15f;

	[Header("์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ •")]
	public float rotationSpeed = 0.3f;  // ๋‚˜์„  ํšŒ์ „ ์†๋„
	public float waveAmplitude = 0.2f;  // Z ๋ฐฉํ–ฅ ํŒŒ๋™ ์ง„ํญ
	public float waveFrequency = 2.0f;  // ํŒŒ๋™ ์ฃผํŒŒ์ˆ˜

	[Header("์ƒ‰์ƒ")]
	[Range(0f, 1f)] public float hueBase = 0.46f;
	[Range(0f, 1f)] public float hueRange = 0.24f;
	[Range(0f, 1f)] public float saturation = 0.4f;
	[Range(0f, 1f)] public float brightness = 1.0f;

	// -----------------------------------------------------------
	// ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์บ์‹œ (์žฌ์ƒ์„ฑ ํŠธ๋ฆฌ๊ฑฐ ํŒ๋‹จ์šฉ)
	// -----------------------------------------------------------

	private int _cachedCubeCount;
	private float _cachedAngleStep;
	private float _cachedRadiusStep;
	private float _cachedDepthStep;
	private float _cachedCubeScale;

	// -----------------------------------------------------------
	// ํ๋ธŒ ๋ฐ์ดํ„ฐ
	// -----------------------------------------------------------

	struct CubeData
	{
		public Transform tr;
		public Material mat;
		public float baseAngle;   // ์ƒ์„ฑ ์‹œ์ ์˜ ๋‚˜์„  ๊ฐ๋„
		public float radius;      // ๋‚˜์„  ๋ฐ˜์ง€๋ฆ„
		public float baseDepth;   // Z ๊ธฐ๋ณธ๊ฐ’
		public float normalized;  // 0~1 (์ธ๋ฑ์Šค ์ •๊ทœํ™”, ์ƒ‰์ƒ ๊ณ„์‚ฐ์šฉ)
	}

	// ๋ฐฐ์—ด๋กœ ์„ ์–ธํ•ด ref ์ธ๋ฑ์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ (struct ๋ถˆํ•„์š”ํ•œ ๋ณต์‚ฌ ๋ฐฉ์ง€)
	CubeData[] _cubes = new CubeData[0];

	// ๊ณต์œ  ๋ฉ”์‹œ (๋ชจ๋“  ํ๋ธŒ๊ฐ€ ๋™์ผํ•œ Mesh ์—์…‹์„ ์ฐธ์กฐ)
	private Mesh _sharedCubeMesh;

	// waveFrequency * PI * 2 ์บ์‹œ (๋งค ํ”„๋ ˆ์ž„ ๋ฐ˜๋ณต ๊ณ„์‚ฐ ๋ฐฉ์ง€)
	private float _waveFreqFull;

#if UNITY_EDITOR
    private bool _generateQueued = false;
#endif

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

	void OnEnable()
	{
		// ์ด๋ฏธ ํ๋ธŒ๊ฐ€ ์žˆ์œผ๋ฉด ์žฌ์ƒ์„ฑ ๊ฑด๋„ˆ๋œ€ (์”ฌ ๋กœ๋“œยทํ”Œ๋ ˆ์ด ์ „ํ™˜ ์‹œ ์ค‘๋ณต ๋ฐฉ์ง€)
		if (_cubes.Length == 0)
			Generate();

		CacheStructureParams();
		CacheWaveFreq();
	}

	void OnDisable()
	{
		// ExecuteAlways ํ™˜๊ฒฝ์—์„œ ์ปดํฌ๋„ŒํŠธ ๋น„ํ™œ์„ฑํ™” ์‹œ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
		Clear();
	}

	void OnDestroy()
	{
		Clear();
	}

	void OnValidate()
	{
#if UNITY_EDITOR
        CacheWaveFreq();

        if (StructureParamsChanged())
        {
            CacheStructureParams();
            QueueGenerate();
        }
        else
        {
            // ์ƒ‰์ƒยท์• ๋‹ˆ๋ฉ”์ด์…˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ: ์ฆ‰์‹œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฐฑ์‹ 
            ApplyAnimation(0f);
        }
#endif
	}

	void Update()
	{
		// ์—๋””ํ„ฐ Scene ๋ทฐ์—์„œ๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ •์ง€ (๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” t=0 ๊ณ ์ •)
#if UNITY_EDITOR
        if (!Application.isPlaying) return;
#endif
		ApplyAnimation(Time.time);
	}

	// -----------------------------------------------------------
	// ์—๋””ํ„ฐ ์ง€์—ฐ ์ƒ์„ฑ (OnValidate ๋‚ด ์ง์ ‘ ์ƒ์„ฑ ๊ธˆ์ง€ ์šฐํšŒ)
	// -----------------------------------------------------------

#if UNITY_EDITOR
    void QueueGenerate()
    {
        if (_generateQueued) return;

        _generateQueued = true;
        EditorApplication.delayCall += () =>
        {
            _generateQueued = false;
            if (this == null) return;
            Generate();
        };
    }
#endif

	// -----------------------------------------------------------
	// ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€๊ฒฝ ๊ฐ์ง€
	// -----------------------------------------------------------

	bool StructureParamsChanged()
	{
		return cubeCount != _cachedCubeCount
				|| angleStep != _cachedAngleStep
				|| radiusStep != _cachedRadiusStep
				|| depthStep != _cachedDepthStep
				|| cubeScale != _cachedCubeScale;
	}

	void CacheStructureParams()
	{
		_cachedCubeCount = cubeCount;
		_cachedAngleStep = angleStep;
		_cachedRadiusStep = radiusStep;
		_cachedDepthStep = depthStep;
		_cachedCubeScale = cubeScale;
	}

	// waveFrequency ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๊ฐฑ์‹ 
	void CacheWaveFreq()
	{
		_waveFreqFull = waveFrequency * Mathf.PI * 2f;
	}

	// -----------------------------------------------------------
	// ์ƒ์„ฑ
	// -----------------------------------------------------------

	void Generate()
	{
		Clear();

		// Shader ๋ฅผ ๋ฃจํ”„ ๋ฐ–์—์„œ ํ•œ ๋ฒˆ๋งŒ ํƒ์ƒ‰
		Shader litShader = Shader.Find("Universal Render Pipeline/Lit");
		if (litShader == null)
		{
			Debug.LogError("[SpiralCubeGenerator] URP Lit ์…ฐ์ด๋”๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. URP ํ”„๋กœ์ ํŠธ์ธ์ง€ ํ™•์ธํ•˜์„ธ์š”.");
			return;
		}

		// ๊ณต์œ  ํ๋ธŒ Mesh ํš๋“ (์—†์œผ๋ฉด ๋นŒํŠธ์ธ ์—์…‹์—์„œ ๊ฐ€์ ธ์˜ด)
		_sharedCubeMesh = GetBuiltinCubeMesh();
		if (_sharedCubeMesh == null)
		{
			Debug.LogError("[SpiralCubeGenerator] ๊ธฐ๋ณธ ํ๋ธŒ Mesh ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
			return;
		}

		_cubes = new CubeData[cubeCount];

		for (int i = 0; i < cubeCount; i++)
		{
			float t = (cubeCount > 1) ? (float)i / (cubeCount - 1) : 0f;
			float angle = i * angleStep;
			float r = i * radiusStep;
			float z = i * depthStep;

			Vector3 pos = new Vector3(
					Mathf.Cos(angle) * r,
					Mathf.Sin(angle) * r,
					z
			);

			_cubes[i] = SpawnCube(i, pos, angle, r, z, t, litShader);
		}

		ApplyAnimation(0f);
	}

	// ํ๋ธŒ GameObject ๋ฅผ ์ˆ˜๋™์œผ๋กœ ๊ตฌ์„ฑํ•ด ๋ถˆํ•„์š”ํ•œ Collider ์ƒ์„ฑ์„ ๋ง‰์Œ
	CubeData SpawnCube(int index, Vector3 pos, float angle, float radius, float depth, float normalized, Shader shader)
	{
		GameObject go = new GameObject($"Cube_{index}");
		go.transform.SetParent(transform, false);
		go.transform.localPosition = pos;
		go.transform.localScale = Vector3.one * cubeScale;

		// MeshFilter: ๋ชจ๋“  ํ๋ธŒ๊ฐ€ ๋™์ผํ•œ Mesh ๋ฅผ ๊ณต์œ  (sharedMesh)
		MeshFilter mf = go.AddComponent<MeshFilter>();
		mf.sharedMesh = _sharedCubeMesh;

		// MeshRenderer: ๋จธํ‹ฐ๋ฆฌ์–ผ์€ ์ธ์Šคํ„ด์Šค๋ณ„๋กœ ๋ณ„๋„ ์ƒ์„ฑ (์ƒ‰์ƒ ๊ฐœ๋ณ„ ์ œ์–ด)
		MeshRenderer mr = go.AddComponent<MeshRenderer>();
		Material mat = new Material(shader);
		mat.EnableKeyword("_EMISSION");
		mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
		mr.material = mat;

		return new CubeData
		{
			tr = go.transform,
			mat = mat,
			baseAngle = angle,
			radius = radius,
			baseDepth = depth,
			normalized = normalized
		};
	}

	// ์—๋””ํ„ฐ ๋นŒํŠธ์ธ ํ๋ธŒ Mesh ๋ฅผ ์ž„์‹œ ์˜ค๋ธŒ์ ํŠธ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ ํ›„ ์ฆ‰์‹œ ํŒŒ๊ดด
	Mesh GetBuiltinCubeMesh()
	{
		GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
		Mesh mesh = temp.GetComponent<MeshFilter>().sharedMesh;
		DestroyImmediate(temp);
		return mesh;
	}

	// -----------------------------------------------------------
	// ์ •๋ฆฌ
	// -----------------------------------------------------------

	void Clear()
	{
		// ๋จธํ‹ฐ๋ฆฌ์–ผ ๋จผ์ € ํ•ด์ œ (GC ์— ๋งก๊ธฐ๋ฉด ์—๋””ํ„ฐ์—์„œ ๋ˆ„์ˆ˜)
		if (_cubes != null)
		{
			for (int i = 0; i < _cubes.Length; i++)
			{
				if (_cubes[i].mat != null)
					DestroyImmediate(_cubes[i].mat);
			}
		}

		_cubes = new CubeData[0];

		// ์ž์‹ ์˜ค๋ธŒ์ ํŠธ ํŒŒ๊ดด
		while (transform.childCount > 0)
		{
#if UNITY_EDITOR
            DestroyImmediate(transform.GetChild(0).gameObject);
#else
			Destroy(transform.GetChild(0).gameObject);
#endif
		}
	}

	// -----------------------------------------------------------
	// ์• ๋‹ˆ๋ฉ”์ด์…˜
	// -----------------------------------------------------------

	void ApplyAnimation(float t)
	{
		for (int i = 0; i < _cubes.Length; i++)
		{
			ref CubeData c = ref _cubes[i];  // ref: ๊ตฌ์กฐ์ฒด ๋ณต์‚ฌ ์—†์ด ์ง์ ‘ ์ ‘๊ทผ
			if (c.tr == null || c.mat == null) continue;

			// ๋‚˜์„  ์ „์ฒด๋ฅผ Z์ถ• ์ค‘์‹ฌ์œผ๋กœ ์ฒœ์ฒœํžˆ ํšŒ์ „
			float animAngle = c.baseAngle + t * rotationSpeed;

			// Z์ถ• ํŒŒ๋™: ๊ฐ ํ๋ธŒ๊ฐ€ ๋ฌผ๊ฒฐ์น˜๋“ฏ ์•ž๋’ค๋กœ ์›€์ง์ž„
			float wave = Mathf.Sin(c.normalized * _waveFreqFull - t) * waveAmplitude;

			c.tr.localPosition = new Vector3(
					Mathf.Cos(animAngle) * c.radius,
					Mathf.Sin(animAngle) * c.radius,
					c.baseDepth + wave
			);

			// ํ๋ธŒ ์ž์ฒด๋„ ๋‚˜์„  ๋ฐฉํ–ฅ์œผ๋กœ ์ž์ „
			c.tr.localRotation = Quaternion.Euler(0f, 0f, animAngle * Mathf.Rad2Deg);

			// ์ƒ‰์ƒ
			float hue = (hueBase + c.normalized * hueRange) % 1f;
			Color albedo = Color.HSVToRGB(hue, saturation, brightness);
			Color emission = Color.HSVToRGB(hue, 1f, c.normalized) * 0.5f;

			c.mat.SetColor("_BaseColor", albedo);
			c.mat.SetColor("_EmissionColor", emission);
		}
	}
}
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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