<|start_filename|>LinesByDistance/Assets/ParticleOnSphere/ParticleOnSphere.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

namespace irishoak.Sample
{
    public class ParticleOnSphere : MonoBehaviour
    {
        #region Structures
        public struct ParticleData
        {
            public Vector3 Velocity;    
            public Vector3 Position;
            public float   Age;
            public float   Pad0;
        };
        #endregion

        #region Constants
        // パーティクルの最大数
        const int NUM_PARTICLES = 16384;
        // スレッドグループ数(X)
        const int NUM_THREAD    = 256;
        #endregion

        #region Properties
        [Range(0.1f, 10.0f), Tooltip("パーティクルの寿命（最小値）")]
        public float LifeMin = 1.0f;
        [Range(0.1f, 10.0f), Tooltip("パーティクルの寿命（最大値）")]
        public float LifeMax = 2.0f;
        [Range(0.0f, 10.0f), Tooltip("速度")]
        public float Velocity = 0.1f;
        [Range(1.0f, 10.0f), Tooltip("パーティクルを生成するエリアの半径")]
        public float SphereRadius = 5.0f;
        [Range(1, 10), Tooltip("サーフェスの層の数")]
        public int SurfaceLayerNum = 10;

        [Range(0.0f, 1.0f), Tooltip("放出量")]
        public float Throttle = 1.0f;

        [Tooltip("パーティクルのテクスチャ")]
        public Texture2D ParticleTex = null;
        [Tooltip("パーティクルのサイズ")]
        public float ParticleSize = 0.025f;
        [Tooltip("パーティクルのカラー")]
        public Color ParticleColor = Color.white;
        #endregion

        #region Public Resources and References
        [Tooltip("パーティクル位置更新のためのComputeShader")]
        public ComputeShader KernelCS;
        [Tooltip("パーティクルの描画のためのシェーダ")]
        public Shader RenderShader = null;
        [Tooltip("レンダリングに使用するカメラの参照（ビルボードに使用）")]
        public Camera RenderCamera = null;
        [Tooltip("スクリプトの参照")]
        public LinesByDistance LinesByDistanceScript = null;
        #endregion

        #region Private Resources and Variables
        // パーティクルのデータのバッファ
        ComputeBuffer _particleBuffer;
        // パーティクルの頂点データのバッファ
        ComputeBuffer _vertexBuffer;
        // パーティクル描画のマテリアル
        Material _particleRenderMat = null;
        #endregion

        #region MonoBehaviour
        void Start()
        {
            InitResources();
        }

        void Update()
        {
            if (LinesByDistanceScript != null)
            {
                // VertexBufferのパーティクルの位置データをコピー
                int id = KernelCS.FindKernel("CSCopyVertex");
                KernelCS.SetBuffer(id, "_VertexBuffer",   _vertexBuffer);
                KernelCS.SetBuffer(id, "_ParticleBuffer", _particleBuffer);
                KernelCS.Dispatch(id, NUM_THREAD, 1, 1);

                // Lineスクリプトに頂点バッファをセット
                LinesByDistanceScript.SetVertexBuffer(ref _vertexBuffer);
            }

            UpdateParticles();
        }

        void OnRenderObject()
        {
            DrawParticles();
        }

        void OnDestroy()
        {
            ReleaseResources();
        }
        #endregion

        #region Private Functions
        /// <summary>
        /// リソースを初期化
        /// </summary>
        void InitResources()
        {
            _particleBuffer = new ComputeBuffer(NUM_PARTICLES, Marshal.SizeOf(typeof(ParticleData)));

            var particleDataArr = new ParticleData[NUM_PARTICLES];
            for (var i = 0; i < particleDataArr.Length; i++)
            {
                particleDataArr[i].Velocity = UnityEngine.Random.insideUnitSphere * Velocity;
                particleDataArr[i].Position = UnityEngine.Random.insideUnitSphere * SphereRadius;
                particleDataArr[i].Age      = 0.0f;
                particleDataArr[i].Pad0     = 0.0f;
            }
            _particleBuffer.SetData(particleDataArr);
            particleDataArr = null;

            _vertexBuffer = new ComputeBuffer(NUM_PARTICLES, Marshal.SizeOf(typeof(Vector3)));     
        }

        /// <summary>
        /// リソースを解放
        /// </summary>
        void ReleaseResources()
        {
            if (_particleBuffer != null)
            {
                _particleBuffer.Release();
                _particleBuffer = null;
            }

            if (_vertexBuffer != null)
            {
                _vertexBuffer.Release();
                _vertexBuffer = null;
            }

            if (_particleRenderMat != null)
            {
                Material.DestroyImmediate(_particleRenderMat);
                _particleRenderMat = null;
            }
        }

        /// <summary>
        /// パーティクルを更新
        /// </summary>
        void UpdateParticles()
        {
            ComputeShader cs = KernelCS;
            int kernelId = cs.FindKernel("CSUpdate");

            cs.SetBuffer(kernelId, "_ParticleBuffer", _particleBuffer);
            cs.SetVector("_LifeParams", new Vector4(1.0f/Mathf.Max(LifeMin, 0.0001f), 1.0f/Mathf.Max(LifeMax, 0.0001f), 0, 0));
            cs.SetFloat ("_Velocity",   Velocity);
            cs.SetFloat ("_Radius",     SphereRadius);
            cs.SetFloat ("_SurfaceLayerNum", SurfaceLayerNum);
            cs.SetFloat ("_DeltaTime",  Time.deltaTime);
            cs.SetFloat ("_Timer",      Time.timeSinceLevelLoad);
            cs.SetFloat ("_Throttle",   Throttle);

            cs.Dispatch(kernelId, NUM_THREAD, 1, 1);
        }

        /// <summary>
        /// パーティクルを描画
        /// </summary>
        void DrawParticles()
        {
            if (_particleRenderMat == null)
            {
                _particleRenderMat = new Material(RenderShader);
                _particleRenderMat.hideFlags = HideFlags.HideAndDontSave;
            }

            var invViewMatrix = RenderCamera.worldToCameraMatrix.inverse;

            _particleRenderMat.SetPass(0);
            _particleRenderMat.SetTexture("_MainTex",        ParticleTex);
            _particleRenderMat.SetFloat  ("_ParticleRad",    ParticleSize);
            _particleRenderMat.SetColor  ("_Color",          ParticleColor);
            _particleRenderMat.SetMatrix ("_InvViewMatrix",  invViewMatrix);
            _particleRenderMat.SetBuffer ("_ParticleBuffer", _particleBuffer);

            Graphics.DrawProcedural(MeshTopology.Points, NUM_PARTICLES);
        }

        #endregion
    }
}

<|start_filename|>LinesByDistance/Assets/LinesByDistance/Scripts/LinesByDistance.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

namespace irishoak.Sample
{
    public class LinesByDistance : MonoBehaviour
    {

        #region Structs
        public struct LineData
        {
            public Vector3 Position0;   // ラインの端点,点0の位置
            public float   Alpha0;      // ラインの端点,点0の不透明度
            public Vector3 Position1;   // ラインの端点,点1の位置
            public float   Alpha1;      // ラインの端点,点1の不透明度
        };
        #endregion

        #region Constants
        // ラインの最大数
        const int MAX_LINE_NUM         = 524288;
        // 頂点の最大数
        const int MAX_VERTEX_NUM       = 16384;
        // 生成されるラインを制限するためのカウンタの最大数
        const int MAX_LINE_COUNTER_NUM = 2097152;
        
        // スレッドグループ数(X)
        const int MUM_THREAD = 256; 
        #endregion

        #region Public Resources
        [Tooltip("ラインの生成を行うComputeShader")]
        public ComputeShader KernelCS;
        [Tooltip("ラインを描画するShader")]
        public Shader LineRenderShader;
        #endregion

        #region Properties
        [Range(0.0f, 10.0f), Tooltip("ラインを結ぶ最小距離")]
        public float MinDist = 0.1f;
        [Range(0.0f, 10.0f), Tooltip("ラインを結ぶ最大距離")]
        public float MaxDist = 0.3f;
        [Tooltip("ラインの色")]
        public Color LineColor = Color.white;

        [Tooltip("頂点バッファの初期化を行うか（他のスクリプトから頂点バッファの代入を行う場合はfalseに）")]
        public bool IsEnableInitVertexBuffer = false;
        [Tooltip("GUIにデバッグ用テキストを表示するか")]
        public bool EnableDrawDebugTextOnGUI = false;
        #endregion

        #region Private Variables and Resources
        // 頂点を格納するバッファ
        ComputeBuffer _vertexBuffer;
        // ラインデータを格納するバッファ              
        ComputeBuffer _lineDataBuffer;
        // 生成されたラインをカウントするバッファ
        ComputeBuffer _generatedLineCounterBuffer;
        // ラインのIndirect描画のための変数を扱うバッファ
        ComputeBuffer _drawLinesIndirectArgsBuffer;

        // ラインのIndirect描画のための変数
        int[] _drawLinesIndirectArgs;

        // 現在のラインの数
        int _currentLinesNum = 0;

        // ラインを描画するマテリアル
        Material _lineRenderMat;
        #endregion

        #region Accessor
        /// <summary>
        /// 頂点バッファをセット
        /// </summary>
        /// <param name="buffer"></param>
        public void SetVertexBuffer(ref ComputeBuffer buffer)
        {
            this._vertexBuffer = buffer;
        }
        #endregion

        #region MonoBehaviour Functions
        void Start()
        {
            InitResources();
        }

        void Update()
        {
            UpdateLines();
        }

        void OnRenderObject()
        {
            DrawLines();
        }

        void OnDestroy()
        {
            ReleaseResources();
        }

        void OnGUI()
        {
            if (!EnableDrawDebugTextOnGUI) return;
            
            int restoreFontSize = GUI.skin.label.fontSize;
            GUI.skin.label.fontSize = 24;
            GUI.Label(new Rect(32, 32, 512, 64), "CurrentLineNum : " + _currentLinesNum.ToString());
            GUI.skin.label.fontSize = restoreFontSize;

        }
        #endregion

        #region Private Functions
        /// <summary>
        /// リソースを初期化
        /// </summary>
        void InitResources()
        {
            _drawLinesIndirectArgs = new int[4] { 0, 1, 0, 0 };

            // --- ComputeBuffer ---
            if (IsEnableInitVertexBuffer)
            {
                var vertexArr = new Vector3[MAX_VERTEX_NUM];
                for (int i = 0; i < vertexArr.Length; i++)
                {
                    vertexArr[i] = UnityEngine.Random.insideUnitSphere * 2.0f;
                }

                _vertexBuffer = new ComputeBuffer(MAX_VERTEX_NUM, Marshal.SizeOf(typeof(Vector3)));
                _vertexBuffer.SetData(vertexArr);

                vertexArr = null;
            }

            _lineDataBuffer = new ComputeBuffer(MAX_LINE_NUM, Marshal.SizeOf(typeof(LineData)), ComputeBufferType.Append); // AppendStructuredBufferとして初期化
            _lineDataBuffer.SetCounterValue(0); // カウンタリセット

            _generatedLineCounterBuffer = new ComputeBuffer(MAX_LINE_COUNTER_NUM, Marshal.SizeOf(typeof(int)), ComputeBufferType.Counter);  // CounterBufferとして初期化
            _generatedLineCounterBuffer.SetCounterValue(0); // カウンタリセット

            _drawLinesIndirectArgsBuffer = new ComputeBuffer(4, Marshal.SizeOf(typeof(int)), ComputeBufferType.IndirectArguments);
            
        }

        /// <summary>
        /// リソースを解放
        /// </summary>
        void ReleaseResources()
        {
            if(_vertexBuffer != null)
            {
                _vertexBuffer.Release();
                _vertexBuffer = null;
            }

            if(_lineDataBuffer != null)
            {
                _lineDataBuffer.Release();
                _lineDataBuffer = null;
            }

            if(_generatedLineCounterBuffer != null)
            {
                _generatedLineCounterBuffer.Release();
                _generatedLineCounterBuffer = null;
            }

            if (_drawLinesIndirectArgsBuffer != null)
            {
                _drawLinesIndirectArgsBuffer.Release();
                _drawLinesIndirectArgsBuffer = null;
            }

            if (_lineRenderMat != null)
            {
                Material.DestroyImmediate(_lineRenderMat);
                _lineRenderMat = null;
            }

        }

        /// <summary>
        /// ラインのデータを更新
        /// </summary>
        void UpdateLines()
        {
            if (_vertexBuffer == null) return;

            _generatedLineCounterBuffer.SetCounterValue(0);
            _lineDataBuffer.SetCounterValue(0);

            ComputeShader cs = KernelCS;
            //int id = cs.FindKernel("CSUpdate");
            int id = cs.FindKernel("CSUpdate_Shared");

            cs.SetFloat("_MinDist", MinDist);
            cs.SetFloat("_MaxDist", MaxDist);
            cs.SetInt  ("_VertexNum", MAX_VERTEX_NUM);
            cs.SetInt  ("_MaxLineNum", MAX_LINE_NUM);
            cs.SetBuffer(id, "_VertexBufferRead", _vertexBuffer);
            cs.SetBuffer(id, "_LineBufferWrite", _lineDataBuffer);
            cs.SetBuffer(id, "_CounterBuffer", _generatedLineCounterBuffer);

            cs.Dispatch(id, MAX_VERTEX_NUM / MUM_THREAD, 1, 1);
        }

        /// <summary>
        /// ラインを描画
        /// </summary>
        void DrawLines()
        {
            if (_lineRenderMat == null)
            {
                _lineRenderMat = new Material(LineRenderShader);
                _lineRenderMat.hideFlags = HideFlags.HideAndDontSave;
            }

            // ラインの数を取得
            _currentLinesNum = GetCurrentLineNum();

            _lineRenderMat.SetPass(0);
            _lineRenderMat.SetColor("_Color", LineColor);
            _lineRenderMat.SetBuffer("_LineDataBuffer", _lineDataBuffer);
            Graphics.DrawProceduralIndirect(MeshTopology.Points, _drawLinesIndirectArgsBuffer, 0);
        }

        /// <summary>
        /// ラインの数を取得
        /// </summary>
        /// <returns>ラインの数</returns>
        int GetCurrentLineNum()
        {
            // CPU->GPU
            _drawLinesIndirectArgsBuffer.SetData(_drawLinesIndirectArgs);
            // ライン数を取得
            ComputeBuffer.CopyCount(_lineDataBuffer, _drawLinesIndirectArgsBuffer, 0);
            // GPU->CPU
            _drawLinesIndirectArgsBuffer.GetData(_drawLinesIndirectArgs);
            return _drawLinesIndirectArgs[0];
        }

        #endregion
    }
}

<|start_filename|>LinesByDistance/Assets/LinesByDistance/Shaders/LineRender.shader<|end_filename|>
﻿Shader "Hidden/irishoak/LinesByDistance/LineRender"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
	}

	CGINCLUDE
	#include "UnityCG.cginc"

	struct lineData
	{
		float3 position0;
		float  alpha0;
		float3 position1;
		float  alpha1;
	};

	struct v2g
	{
		float4 vertex0 : POSITION;
		float4 vertex1 : TEXCOORD2;
		float  alpha0  : TEXCOORD0;
		float  alpha1  : TEXCOORD1;
	};

	struct g2f
	{
		float4 position : SV_POSITION;
		float4 color    : COLOR;
	};

	sampler2D _MainTex;
	float4 _MainTex_ST;

	StructuredBuffer<lineData> _LineDataBuffer;

	float4 _Color;

	// --------------------------------------------------------------------
	// Vertex Shader
	// --------------------------------------------------------------------
	v2g vert(uint id : SV_VertexID)
	{
		v2g o = (v2g)0;
		o.vertex0 = UnityObjectToClipPos(float4(_LineDataBuffer[id].position0, 1.0));
		o.alpha0  = _LineDataBuffer[id].alpha0;
		o.vertex1 = UnityObjectToClipPos(float4(_LineDataBuffer[id].position1, 1.0));
		o.alpha1  = _LineDataBuffer[id].alpha1;
		return o;
	}

	// --------------------------------------------------------------------
	// Geometry Shader
	// --------------------------------------------------------------------
	[maxvertexcount(2)]
	void geom(point v2g points[1], inout LineStream<g2f> stream)
	{
		g2f o = (g2f)0;

		float4 pos0 = points[0].vertex0;
		float4 pos1 = points[0].vertex1;

		float a0 = points[0].alpha0;
		float a1 = points[0].alpha1;

		o.color = float4(1, 1, 1, 1) * a0;
		o.position = pos0;
		stream.Append(o);

		o.color = float4(1, 1, 1, 1) * a1;
		o.position = pos1;
		stream.Append(o);

		stream.RestartStrip();
	}

	// --------------------------------------------------------------------
	// Fragment Shader
	// --------------------------------------------------------------------
	fixed4 frag(g2f i) : SV_Target
	{
		fixed4 col = i.color * _Color;
		return col;
	}
	ENDCG

	SubShader
	{
		Tags{ "RenderType" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		LOD 100
		
		Blend One One
		ZWrite Off

		Pass
		{
			CGPROGRAM
			#pragma target   5.0
			#pragma vertex   vert
			#pragma geometry geom
			#pragma fragment frag
			ENDCG
		}
	}
}


<|start_filename|>LinesByDistance/Assets/ParticleOnSphere/ParticleRender.shader<|end_filename|>
﻿Shader "Hidden/irishoak/ParticleOnSphere/ParticleRender"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_ParticleRad("ParticleRadius", Float) = 0.05
	    _Color("Particle Color", Color) = (1,1,1,1)
	}

	CGINCLUDE
	#include "UnityCG.cginc"

	struct v2g
	{
		float3 pos   : POSITION_SV;
		float4 color : COLOR;
	};

	struct g2f
	{
		float4 pos   : POSITION;
		float2 tex   : TEXCOORD0;
		float4 color : COLOR;
	};

	struct ParticleData
	{
		float3 velocity;
		float3 position;
		float  age;
		float  pad0;
	};

	sampler2D _MainTex;
	float4 _MainTex_ST;

	StructuredBuffer<ParticleData> _ParticleBuffer;

	float    _ParticleRad;
	float4x4 _InvViewMatrix;
	fixed4   _Color;
	
	// --------------------------------------------------------------------
	// Vertex Shader
	// --------------------------------------------------------------------
	v2g vert(uint id : SV_VertexID)
	{
		v2g o = (v2g)0;
		o.pos   = _ParticleBuffer[id].position;
		float age = _ParticleBuffer[id].age;
		o.color = saturate(min(1.0, 5.0 - abs(5.0 - age * 10)));
		return o;
	}

	static const float3 g_positions[4] =
	{
		float3(-1, 1, 0),
		float3( 1, 1, 0),
		float3(-1,-1, 0),
		float3( 1,-1, 0),
	};

	static const float2 g_texcoords[4] =
	{
		float2(0, 0),
		float2(1, 0),
		float2(0, 1),
		float2(1, 1),
	};

	// --------------------------------------------------------------------
	// Geometry Shader
	// --------------------------------------------------------------------
	[maxvertexcount(4)]
	void geom(point v2g In[1], inout TriangleStream<g2f> SpriteStream)
	{
		g2f output = (g2f)0;
		[unroll]
		for (int i = 0; i < 4; i++)
		{
			float3 position = g_positions[i] * _ParticleRad;
			position = mul(_InvViewMatrix, position) + In[0].pos;
			output.pos = mul(UNITY_MATRIX_MVP, float4(position, 1.0));

			output.color = In[0].color;
			output.tex = g_texcoords[i];
			SpriteStream.Append(output);
		}
		SpriteStream.RestartStrip();
	}

	// --------------------------------------------------------------------
	// Fragment Shader
	// --------------------------------------------------------------------
	fixed4 frag(g2f input) : SV_Target
	{
		return tex2D(_MainTex, input.tex) * input.color * _Color;
	}
	ENDCG

	SubShader
	{
		Tags{ "RenderType" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		LOD 100

		ZWrite Off
		Blend One One

		Pass
		{
			CGPROGRAM
			#pragma target   5.0
			#pragma vertex   vert
			#pragma geometry geom
			#pragma fragment frag
			ENDCG
		}
	}
}