Shader "CX/Plexus Line"
{
Properties
{
_Color ("Color", Color) = (0, 1, 0, 1)
[HDR] _Emission1 ("Emission1", Color) = (2.56, 0, 0, 1)
[HDR] _Emission2 ("Emission2", Color) = (0, 1.95, 2.52, 1)
_BoxDims ("Box Dimensions", float) = (5, 5, 5, 1) // Controlled by Plexus.cs
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Cull [_Cull]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 col : TEXCOORD0;
};
fixed4 _Color;
fixed4 _Emission1, _Emission2;
half4 _BoxDims;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.col = fixed4((clamp(o.vertex.xyz/_BoxDims.xyz, -1, 1) + 1.0) / 2.0, 1);
return o;
}
fixed4 pixel;
fixed4 frag (v2f i) : SV_Target
{
pixel = _Color + lerp(_Emission1, _Emission2, i.col);
return pixel;
}
ENDCG
}
}
FallBack "Diffuse"
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[ExecuteAlways]
public class Plexus : MonoBehaviour
{
public ComputeShader plexus;
public int amountOfPoints = 100;
public int PPPS = 2; // Processed Points Per Second
public float lineWidth = 0.02f;
public Material lineMaterial;
public Vector3 box = new Vector3(4, 4, 4);
public float particleSpeed = 1.0f;
public float maxConnDistance = 3.0f; // The maximum distance for two points to connect
private float maxConnDistanceSqr;
private Vector3[] defaultPositions;
private Vector3[] velocities;
private Vector3[] positions;
private Mesh lineMesh;
private void Start()
{
lineMaterial.SetVector("_BoxDims", new Vector4(box.x, box.y, box.z, 1));
positions = new Vector3[amountOfPoints];
defaultPositions = new Vector3[amountOfPoints];
for (int i = 0; i < amountOfPoints; ++i)
{
positions[i] = new Vector3(
Random.Range(-box.x, box.x),
Random.Range(-box.y, box.y),
Random.Range(-box.z, box.z));
defaultPositions[i] = positions[i];
}
lineMesh = new Mesh();
int[] trigs = new int[6];
trigs[0] = 0;
trigs[1] = 1;
trigs[2] = 2;
trigs[3] = 3;
trigs[4] = 2;
trigs[5] = 1;
lineMesh.vertices = verts;
lineMesh.triangles = trigs;
velocities = new Vector3[amountOfPoints];
StartCoroutine(ConnectDots());
}
private void Update()
{
MovePoints();
RenderLines();
}
private void MovePoints()
{
int kernelIndex = plexus.FindKernel("MoveParticels");
// sizeof(float3) == 12
ComputeBuffer positionsBuffer = new ComputeBuffer(positions.Length, 12);
positionsBuffer.SetData(positions);
plexus.SetBuffer(kernelIndex, "positions", positionsBuffer);
// sizeof(float3) == 12
ComputeBuffer defaultPositionsBuffer = new ComputeBuffer(defaultPositions.Length, 12);
defaultPositionsBuffer.SetData(defaultPositions);
plexus.SetBuffer(kernelIndex, "defaultPositions", defaultPositionsBuffer);
// sizeof(float3) == 12
ComputeBuffer velocitiesBuffer = new ComputeBuffer(velocities.Length, 12);
velocitiesBuffer.SetData(velocities);
plexus.SetBuffer(kernelIndex, "velocities", velocitiesBuffer);
plexus.SetFloat("deltaTime", Time.deltaTime);
plexus.SetFloat("elapsedTime", Time.time);
plexus.SetFloat("particleSpeed", particleSpeed);
plexus.Dispatch(kernelIndex, positions.Length, 1, 1);
positionsBuffer.GetData(positions);
positionsBuffer.Release();
defaultPositionsBuffer.Release();
velocitiesBuffer.Release();
}
private static float DistanceSqr(Vector3 p1, Vector3 p2)
{
return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + (p1.z - p2.z) * (p1.z - p2.z);
}
Vector3 normal, side, p1, p2;
int startingVerticesIndex = 0;
List<int> lineTrigs = new List<int>();
List<Vector3> lineVerts = new List<Vector3>();
Vector3[] verts = new Vector3[4];
int[] trigs = new int[6];
private void RenderLines()
{
lineMesh = new Mesh();
for (int i = 0; i < connected.Count; ++i)
{
// DrawLine(positions[connected[i].Key], positions[connected[i].Value]);
//transform.position - positions[connected[i].Key], // local to world space
//transform.position - positions[connected[i].Value]); // local to world space
p1 = positions[connected[i].Key];
p2 = positions[connected[i].Value];
normal = Vector3.Cross(p1, p2);
side = Vector3.Cross(normal, p2 - p1);
side.Normalize();
startingVerticesIndex = lineVerts.Count;
verts[0] = p1 + side * (lineWidth / 2);
verts[1] = p1 + side * (lineWidth / -2);
verts[2] = p2 + side * (lineWidth / 2);
verts[3] = p2 + side * (lineWidth / -2);
trigs[0] = startingVerticesIndex;
trigs[1] = trigs[5] = startingVerticesIndex + 1;
trigs[2] = trigs[4] = startingVerticesIndex + 2;
trigs[3] = startingVerticesIndex + 3;
lineVerts.AddRange(verts);
lineTrigs.AddRange(trigs);
}
lineMesh.vertices = lineVerts.ToArray();
lineMesh.triangles = lineTrigs.ToArray();
// Drawing the mesh
lineMesh.RecalculateBounds();
Graphics.DrawMesh(lineMesh, transform.localToWorldMatrix, lineMaterial, 0);
// Emptying the memory
lineTrigs.Clear();
lineVerts.Clear();
}
[HideInInspector]
public bool isEnabled = false;
List<KeyValuePair<int, int>> connected = new List<KeyValuePair<int, int>>();
HashSet<KeyValuePair<int, int>> connectedHashSet = new HashSet<KeyValuePair<int, int>>();
private IEnumerator ConnectDots()
{
// the idea behind this code is to extend the connection of dots in time
// not to do it each frame for all points but instead of doing it each frame for
// N points
WaitForEndOfFrame wfeof = new WaitForEndOfFrame();
int indx = 0, i = 0, j = 0;
Vector3 currentPos;
maxConnDistanceSqr = maxConnDistance * maxConnDistance;
do
{
yield return wfeof;
for (j = 0; j < PPPS; ++j)
{
currentPos = positions[indx];
connected.RemoveAll(x => x.Key == indx || x.Value == indx);
connectedHashSet.RemoveWhere(x => x.Key == indx || x.Value == indx);
for (i = 0; i < amountOfPoints; ++i)
{
if (i == indx)
continue;
if (DistanceSqr(currentPos, positions[i]) < maxConnDistanceSqr)
{
KeyValuePair<int, int> k = new KeyValuePair<int, int>(indx, i);
if(connectedHashSet.Add(k))
connected.Add(new KeyValuePair<int, int>(indx, i));
}
}
++indx;
if (indx >= amountOfPoints)
indx = 0;
}
} while (!isEnabled);
}
}