Aetheria/Assets/Shaders/Compute/Slime/Slime.cs
Reinier Vens 7a9af46820 Gravity Slime
Adjusted the "slime" particles to adjust their parameters according to the current gravitational potential and gradient!
2021-03-28 03:17:55 +01:00

262 lines
9.3 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using MessagePack;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using Random = UnityEngine.Random;
using static Unity.Mathematics.math;
public class Slime : MonoBehaviour
{
#region variables
[Header("Scene Links")]
public Camera TargetCamera;
[Header("Materials")]
public ComputeShader SlimeCompute;
public Material AccumulationMaterial;
public Material ParticleMaterial;
public RenderTexture GravityTexture;
[Header("Performance")]
public int TextureSize = 1024;
public float ZoneRadius = 512;
public int AgentCount = 524288;
public int ParticleCount = 65536;
public int TrailPoints = 8;
[Header("Parameters")]
public SlimeSettings SlimeSettings;
public float ZoneCeiling;
public float ZoneFloor;
public float NoiseFrequency = 1;
public float NoiseAmplitude = 5;
public float ParticleHeightRange;
public float ParticleHeightOffset;
public float TrailStartWidth = .5f;
public float TrailEndWidth = .1f;
public float TrailDamping = .05f;
private const int GROUP_SIZE = 64;
private int _updateAgentsKernel;
private int _diffuseKernel;
private int _particleKernel;
private int _geometryKernel;
private string _settingsHash;
private int IndexBufferSize => ParticleCount * 6 * (TrailPoints - 1);
#endregion
#region buffers
private ComputeBuffer _agentsBuffer;
private RenderTexture _accumulationTexture;
private RenderTexture _previousAccumulationTexture;
private ComputeBuffer _parameterBuffer;
private ComputeBuffer _particlesBuffer;
private ComputeBuffer _indexBuffer;
private ComputeBuffer _vertexBuffer;
#endregion
#region Structs
struct Agent
{
public Vector2 Position;
public float Angle;
}
struct Particle
{
public Vector3 Start;
public Vector3 Middle;
public Vector3 End;
public float Intensity;
}
struct Vertex
{
public Vector3 Position;
public Vector2 UV;
public float Intensity;
}
#endregion
void Start()
{
_updateAgentsKernel = SlimeCompute.FindKernel("UpdateAgents");
_diffuseKernel = SlimeCompute.FindKernel("Diffuse");
_particleKernel = SlimeCompute.FindKernel("CreateAgentParticles");
_geometryKernel = SlimeCompute.FindKernel("CreateParticleGeometry");
_accumulationTexture = new RenderTexture(TextureSize, TextureSize, 1, RenderTextureFormat.RFloat);
_accumulationTexture.enableRandomWrite = true;
_previousAccumulationTexture = new RenderTexture(TextureSize, TextureSize, 1, RenderTextureFormat.RFloat);
_previousAccumulationTexture.enableRandomWrite = true;
AccumulationMaterial.SetTexture("_MainTex", _accumulationTexture);
_agentsBuffer = new ComputeBuffer(AgentCount, Marshal.SizeOf(typeof(Agent)));
var agents = new Agent[AgentCount];
for (var i = 0; i < agents.Length; i++)
{
agents[i] = new Agent
{
Position = Random.insideUnitCircle * ZoneRadius,
Angle = Random.value * Mathf.PI * 2
};
}
_agentsBuffer.SetData(agents);
_parameterBuffer = new ComputeBuffer(1, Marshal.SizeOf(typeof(SlimeSettings)));
_particlesBuffer = new ComputeBuffer(ParticleCount, Marshal.SizeOf(typeof(Particle)));
_vertexBuffer = new ComputeBuffer(ParticleCount * 2 * TrailPoints, Marshal.SizeOf(typeof(Vertex)));
var indexData = new int[IndexBufferSize];
var index = 0;
for (int iTrail = 0; iTrail < ParticleCount; iTrail++)
{
var trailOffset = iTrail * TrailPoints * 2;
for (int iTrailPoint = 0; iTrailPoint < TrailPoints - 1; iTrailPoint++)
{
var offset = trailOffset + iTrailPoint * 2;
indexData[index++] = 0 + offset;
indexData[index++] = 1 + offset;
indexData[index++] = 2 + offset;
indexData[index++] = 2 + offset;
indexData[index++] = 1 + offset;
indexData[index++] = 3 + offset;
}
}
_indexBuffer = new ComputeBuffer(indexData.Length, Marshal.SizeOf(typeof(uint))); // 1 trail point to 2 triangles (6 vertices)
_indexBuffer.SetData(indexData);
}
void UpdateSettings()
{
RegisterResolver.Register();
var hash = MessagePackSerializer.Serialize(SlimeSettings).GetHashSHA1();
if(hash != _settingsHash)
{
_settingsHash = hash;
_parameterBuffer.SetData(new[] {SlimeSettings});
}
}
// Update is called once per frame
void Update()
{
UpdateSettings();
SlimeCompute.SetInt("randomOffset", Random.Range(0,65536));
SlimeCompute.SetInt("textureWidth", TextureSize);
SlimeCompute.SetInt("numAgents", AgentCount);
SlimeCompute.SetFloat("zoneRadius", ZoneRadius);
SlimeCompute.SetFloat("deltaTime", Time.deltaTime);
SlimeCompute.SetBuffer(_updateAgentsKernel, "agents", _agentsBuffer);
SlimeCompute.SetBuffer(_updateAgentsKernel, "parameters", _parameterBuffer);
SlimeCompute.SetTexture(_updateAgentsKernel, "accumulation", _accumulationTexture);
SlimeCompute.SetTexture(_updateAgentsKernel, "Heightmap", GravityTexture);
var numberOfGroups = Mathf.CeilToInt((float)AgentCount / GROUP_SIZE);
SlimeCompute.Dispatch(_updateAgentsKernel, numberOfGroups, 1, 1);
Graphics.Blit(_accumulationTexture, _previousAccumulationTexture);
SlimeCompute.SetBuffer(_diffuseKernel, "parameters", _parameterBuffer);
SlimeCompute.SetTexture(_diffuseKernel, "Heightmap", GravityTexture);
SlimeCompute.SetTexture(_diffuseKernel, "accumulation", _previousAccumulationTexture);
SlimeCompute.SetTexture(_diffuseKernel, "diffusedAccumulation", _accumulationTexture);
numberOfGroups = Mathf.CeilToInt((float)TextureSize / 8);
SlimeCompute.Dispatch(_diffuseKernel, numberOfGroups, numberOfGroups, 1);
SlimeCompute.SetBuffer(_particleKernel, "parameters", _parameterBuffer);
SlimeCompute.SetTexture(_particleKernel, "Heightmap", GravityTexture);
SlimeCompute.SetBuffer(_particleKernel, "agents", _agentsBuffer);
SlimeCompute.SetBuffer(_particleKernel, "particles", _particlesBuffer);
SlimeCompute.SetFloat("time", Time.time * NoiseFrequency);
SlimeCompute.SetFloat("noiseAmplitude", NoiseAmplitude);
SlimeCompute.SetFloat("heightRange", ParticleHeightRange);
SlimeCompute.SetFloat("heightOffset", ParticleHeightOffset);
SlimeCompute.SetFloat("trailDamping", TrailDamping);
SlimeCompute.SetInt("particleCount", ParticleCount);
numberOfGroups = ParticleCount / GROUP_SIZE;
SlimeCompute.Dispatch(_particleKernel, numberOfGroups, 1, 1);
SlimeCompute.SetInt("trailPoints", TrailPoints);
SlimeCompute.SetFloat("trailStartWidth", TrailStartWidth);
SlimeCompute.SetFloat("trailEndWidth", TrailEndWidth);
SlimeCompute.SetVector("cameraPos", TargetCamera.transform.position);
SlimeCompute.SetBuffer(_geometryKernel, "particles", _particlesBuffer);
SlimeCompute.SetBuffer(_geometryKernel, "vertexBuffer", _vertexBuffer);
numberOfGroups = ParticleCount * TrailPoints / GROUP_SIZE;
SlimeCompute.Dispatch(_geometryKernel, numberOfGroups, 1, 1);
ParticleMaterial.SetBuffer("_IndexBuffer", _indexBuffer);
ParticleMaterial.SetBuffer("_VertexBuffer", _vertexBuffer);
Graphics.DrawProcedural(
ParticleMaterial,
new Bounds(Vector3.zero, ZoneRadius * Vector3.one),
MeshTopology.Triangles,
IndexBufferSize,
1,
TargetCamera,
null,
ShadowCastingMode.Off,
false,
0);
}
private void OnDisable()
{
_accumulationTexture.Release();
_agentsBuffer.Release();
}
}
[Serializable, MessagePackObject(keyAsPropertyName:true)]
public struct SlimeSettings
{
public SlimeParameter Deposition;
public SlimeParameter Diffusion;
public SlimeParameter Decay;
public SlimeParameter TurnSpeed;
public SlimeParameter Speed;
public SlimeParameter Drive;
public SlimeParameter SensorDistance;
public SlimeParameter SensorSpread;
public SlimeParameter TrailDamping;
public SlimeParameter Intensity;
}
[Serializable, MessagePackObject(keyAsPropertyName:true)]
public struct SlimeParameter
{
public float HeightSlopeLerp;
public float HeightThreshold;
public float HeightBlend;
public float SlopeThreshold;
public float SlopeBlend;
public float HighValue;
public float LowValue;
public float Evaluate(float height, float slope)
{
var heightValue = lerp(LowValue, HighValue, smoothstep(HeightThreshold - HeightBlend, HeightThreshold + HeightBlend, height));
var slopeValue = lerp(LowValue, HighValue, smoothstep(SlopeThreshold - SlopeBlend, SlopeThreshold + SlopeBlend, slope));
return lerp(heightValue, slopeValue, HeightSlopeLerp);
}
}