CatlikeCoding-Unity/Assets/Script/GPUGraph.cs

111 lines
3.2 KiB
C#

using UnityEngine;
public class GPUGraph : MonoBehaviour
{
[SerializeField]
ComputeShader computeShader = default;
[SerializeField]
Material material = default;
[SerializeField]
Mesh mesh = default;
const int maxResolution = 1000;
[SerializeField, Range(10, maxResolution)]
int resolution = 10;
[SerializeField]
FunctionLibrary.FunctionName function = default;
public enum TransitionMode { Normal, Cycle, Random }
[SerializeField]
TransitionMode transitionMode = TransitionMode.Normal;
[SerializeField, Min(0.0f)]
float functionDuration = 1.0f;
[SerializeField, Min(0.0f)]
float transitionDuration = 1.0f;
float duration = 0.0f;
bool transitioning;
FunctionLibrary.FunctionName transitionFunction;
ComputeBuffer positionsBuffer;
static readonly int positionsId = Shader.PropertyToID("_Positions");
static readonly int resolutionId = Shader.PropertyToID("_Resolution");
static readonly int stepId = Shader.PropertyToID("_Step");
static readonly int timeId = Shader.PropertyToID("_Time");
static readonly int transitionProgressId = Shader.PropertyToID("_TransitionProgress");
private void OnEnable()
{
positionsBuffer = new ComputeBuffer(maxResolution * maxResolution, 3 * 4);
}
private void OnDisable()
{
positionsBuffer.Release();
positionsBuffer = null;
}
private void Update()
{
duration += Time.deltaTime;
if (transitioning)
{
if (duration >= transitionDuration)
{
duration -= transitionDuration;
transitioning = false;
}
}
else if (duration >= functionDuration)
{
duration -= functionDuration;
transitioning = true;
transitionFunction = function;
PickNextFunction();
}
UpdateFunctionOnGPU();
}
private void PickNextFunction()
{
if (transitionMode == TransitionMode.Normal) return;
function = transitionMode == TransitionMode.Cycle ?
FunctionLibrary.GetNextFunctionName(function) :
FunctionLibrary.GetRandomFunctionNameOtherThan(function);
}
private void UpdateFunctionOnGPU()
{
var step = 2f / resolution;
computeShader.SetInt(resolutionId, resolution);
computeShader.SetFloat(stepId, step);
computeShader.SetFloat(timeId, Time.time);
if (transitioning)
{
computeShader.SetFloat(transitionProgressId, Mathf.SmoothStep(0f, 1f, duration / transitionDuration));
}
var kernelIndex = (int)function +
(int)(transitioning ? transitionFunction : function) * FunctionLibrary.functionCount;
computeShader.SetBuffer(kernelIndex, positionsId, positionsBuffer);
var groups = Mathf.CeilToInt(resolution / 8f);
computeShader.Dispatch(kernelIndex, groups, groups, 1);
material.SetBuffer(positionsId, positionsBuffer);
material.SetFloat(stepId, step);
var bounds = new Bounds(Vector3.zero, Vector3.one * (2f + 2f / resolution));
Graphics.DrawMeshInstancedProcedural(mesh, 0, material, bounds, resolution * resolution);
}
}