Created
January 31, 2020 17:53
-
-
Save amygoodchild/d0a86b3b936183fe93c3f44f2ae55ce4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Reset | |
#pragma kernel ResetTextureKernel | |
#pragma kernel ResetAgentsKernel | |
// Step | |
#pragma kernel MoveAgentsKernel | |
#pragma kernel WriteTrailsKernel | |
#pragma kernel DiffuseTextureKernel | |
// Render | |
#pragma kernel AgentsDebugKernel | |
#pragma kernel RenderKernel | |
Texture2D<float4> readTex; | |
SamplerState sampler_readTex; | |
RWTexture2D<float4> writeTex; | |
RWTexture2D<float4> outTex; | |
RWTexture2D<float4> debugTex; | |
struct Agent | |
{ | |
float2 position; | |
float2 direction; | |
}; | |
RWStructuredBuffer<Agent> agentsBuffer; | |
uint rez; | |
uint stepn; | |
float time; | |
float trailDecayFactor; | |
/* | |
* | |
* | |
* | |
* UTIL | |
* | |
* | |
* | |
*/ | |
// via "Art of Code" on youtube | |
float2 RandomA(float2 p) | |
{ | |
float3 a = frac(p.xyx * float3(123.34, 234.34, 345.65)); | |
a+= dot(a, a + 34.45); | |
return frac(float2(a.x * a.y, a.y * a.z)); | |
} | |
float RandomB(float2 st) { | |
return frac(sin(dot(st.xy, | |
float2(12.9898, 78.233)))* | |
43758.5453123); | |
} | |
float2 RandomDirection(float2 p) | |
{ | |
return(normalize(2.0 * (RandomB(p) - 0.5))); | |
} | |
/* | |
* | |
* | |
* | |
* RESET | |
* | |
* | |
* | |
*/ | |
[numthreads(1, 1, 1)] | |
void ResetTextureKernel(uint3 id : SV_DispatchThreadID) | |
{ | |
writeTex[id.xy] = 0; | |
} | |
[numthreads(64, 1, 1)] | |
void ResetAgentsKernel(uint3 id : SV_DispatchThreadID) | |
{ | |
Agent a; | |
//a.position = RandomA(id.x * .0001 + time * .001) * rez; | |
//a.direction = RandomDirection(id.xx * .01 + sin(time)); | |
//float2 st = float2(randomB(float2((float)id.x*time + .5, id.x)) - 0.5, randomB(float2((float)id.x*time, id.x * 11)) - 0.5) | |
//a.position = RandomB(st) * rez; | |
//a.direction = RandomDirection(st); | |
a.position = float2(RandomB(float2((float)id.x*time* .0001 + .5* .0001, id.x* .0001)), RandomB(float2((float)id.x*time* .0001, id.x * 11* .0001))) * rez; | |
a.direction = float2((RandomB(float2((float)id.x*time* .0001 + .7* .0001, id.x* .0001)) - 0.5), (RandomB(float2((float)id.x*time* .0001, id.x * 3* .0001)) - 0.5)); | |
agentsBuffer[id.x] = a; | |
} | |
/* | |
* | |
* | |
* | |
* STEP | |
* | |
* | |
* | |
*/ | |
[numthreads(1, 1, 1)] | |
void WriteTrailsKernel(uint3 id : SV_DispatchThreadID) | |
{ | |
Agent a = agentsBuffer[id.x]; | |
writeTex[round(a.position)] = clamp(writeTex[round(a.position)] + .1, 0, 1); | |
} | |
/* | |
float2 SimpleTurns(uint3 id, Agent a) { | |
//We will use round whenever reading from or writing to the grid tex | |
float4 f = readTex[round(a.position + a.direction * 2)]; | |
float2 d = a.direction; | |
if (f.x > 0){ | |
d = RandomDirection(id.xx * 0.1 + sin(time)); | |
} | |
return d; | |
} | |
*/ | |
float2 NeighborhoodTurns(uint3 id, Agent a){ | |
float2 vectors[50]; | |
float maxTrail = 0; | |
int range = 1; | |
int i = 0; | |
for (int x = -range; x <= range; x++){ | |
for (int y = -range; y<= range; y++){ | |
if (!(x==0 && y==0)){ | |
float2 direction = float2(x, y); | |
// Hacky but we are just exploring | |
if (dot(normalize(direction), a.direction) > 0){ | |
uint2 coord = round(a.position + direction); | |
float level = readTex.SampleLevel(sampler_readTex, coord / (float)rez, 0).r; | |
if (level == maxTrail) { | |
vectors[i] = normalize(float2(x,y)); | |
i++; | |
} | |
else if (level >= maxTrail){ | |
maxTrail = level; | |
i=0; | |
vectors[i] = normalize(float2(x, y)); | |
i++; | |
} | |
if (stepn % 2 == 1){ | |
// Mark blue the neighbourhood being read | |
debugTex[coord] = float4(0, 0, 1, 0); | |
} | |
} | |
} | |
} | |
} | |
float2 d = a.direction; | |
if(maxTrail >= .1){ | |
int index = 0; | |
d = vectors[index]; | |
} | |
d = normalize(d); | |
// Mark red the next direction | |
if (stepn % 2 == 1){ | |
debugTex[round(a.position + d)] += float4 (1, 0, 0, 0); | |
} | |
return d; | |
} | |
[numthreads(1, 1, 1)] | |
void MoveAgentsKernel(uint3 id : SV_DispatchThreadID) | |
{ | |
Agent a = agentsBuffer[id.x]; | |
// Choose next direction | |
a.direction = NeighborhoodTurns(id, a); | |
// Move Forward | |
a.position = a.position + a.direction; | |
// Boundaries: Wrap | |
if (a.position.x < 0) { | |
a.position.x = rez - 1; | |
} | |
if (a.position.y < 0) { | |
a.position.y = rez - 1; | |
} | |
a.position %= float2(rez, rez); | |
if (stepn % 2 == 0) { | |
agentsBuffer[id.x] = a; | |
} | |
} | |
[numthreads(1, 1, 1)] | |
void DiffuseTextureKernel(uint3 id: SV_DispatchThreadID) | |
{ | |
float4 oc = readTex[id.xy]; | |
float avg = 0; | |
for (int x = -1; x <= 1; x++){ | |
for (int y = -1; y <= 1; y++){ | |
float2 coord = (id.xy + int2(x, y)) / (float)rez; | |
avg += readTex.SampleLevel(sampler_readTex, coord, 0).r; | |
} | |
} | |
avg /= 9.0; | |
oc = avg * trailDecayFactor; | |
oc = clamp(oc, 0, 1); | |
writeTex[id.xy]=oc; | |
} | |
/* | |
* | |
* | |
* | |
* RENDER | |
* | |
* | |
* | |
*/ | |
[numthreads(1, 1, 1)] | |
void RenderKernel(uint3 id : SV_DispatchThreadID) | |
{ | |
outTex[id.xy] = readTex[id.xy]; | |
outTex[id.xy] += debugTex[id.xy]; | |
debugTex[id.xy] = 0; | |
} | |
[numthreads(1, 1, 1)] | |
void AgentsDebugKernel(uint3 id : SV_DispatchThreadID) | |
{ | |
Agent a = agentsBuffer[id.x]; | |
outTex[round(a.position)] += float4(0, .1, 0, 0); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using EasyButtons; | |
[ExecuteAlways] | |
public class TrailAgent : MonoBehaviour | |
{ | |
[Header("Trail Agent Params")] | |
[Range(1, 50000)] | |
public int agentsCount = 1; | |
private ComputeBuffer agentsBuffer; | |
[Range(.7f,1)] | |
public float trailDecayFactor = .9f; | |
[Header("Setup")] | |
[Range(8, 2048)] | |
public int rez = 8; | |
[Range(0,50)] | |
public int stepsPerFrame = 0; | |
[Range(1, 50)] | |
public int stepMod = 1; | |
public Material outMat; | |
public ComputeShader cs; | |
private RenderTexture readTex; | |
private RenderTexture writeTex; | |
private RenderTexture outTex; | |
private RenderTexture debugTex; | |
private int agentsDebugKernel; | |
private int moveAgentsKernel; | |
private int writeTrailsKernel; | |
private int renderKernel; | |
private int diffuseTextureKernel; | |
protected List<ComputeBuffer> buffers; | |
protected List<RenderTexture> textures; | |
protected int stepn = -1; | |
/* | |
* | |
* | |
* RESET | |
* | |
* | |
*/ | |
void Start() | |
{ | |
Reset(); | |
} | |
[Button] | |
public void Reset() | |
{ | |
Release(); | |
agentsDebugKernel = cs.FindKernel("AgentsDebugKernel"); | |
moveAgentsKernel = cs.FindKernel("MoveAgentsKernel"); | |
renderKernel = cs.FindKernel("RenderKernel"); | |
writeTrailsKernel = cs.FindKernel("WriteTrailsKernel"); | |
diffuseTextureKernel = cs.FindKernel("DiffuseTextureKernel"); | |
readTex = CreateTexture(rez, FilterMode.Point); | |
writeTex = CreateTexture(rez, FilterMode.Point); | |
outTex = CreateTexture(rez, FilterMode.Point); | |
debugTex = CreateTexture(rez, FilterMode.Point); | |
agentsBuffer = new ComputeBuffer(agentsCount, sizeof(float) * 4); | |
buffers.Add(agentsBuffer); | |
GPUResetKernel(); | |
Render(); | |
} | |
private void GPUResetKernel() | |
{ | |
int kernel; | |
cs.SetInt("rez", rez); | |
cs.SetFloat("time", Time.time % 1); | |
kernel = cs.FindKernel("ResetTextureKernel"); | |
cs.SetTexture(kernel, "writeTex", writeTex); | |
cs.Dispatch(kernel, rez, rez, 1); | |
cs.SetTexture(kernel, "writeTex", readTex); | |
cs.Dispatch(kernel, rez, rez, 1); | |
kernel = cs.FindKernel("ResetAgentsKernel"); | |
cs.SetBuffer(kernel, "agentsBuffer", agentsBuffer); | |
cs.Dispatch(kernel, agentsCount, 1, 1); | |
} | |
/* | |
* | |
* | |
* STEP | |
* | |
* | |
*/ | |
void Update() | |
{ | |
if (Time.frameCount % stepMod == 0) | |
{ | |
for (int i = 0; i < stepsPerFrame; i++) | |
{ | |
Step(); | |
} | |
} | |
} | |
[Button] | |
public void Step() | |
{ | |
stepn +=1; | |
cs.SetFloat("time", Time.time % 1); | |
cs.SetInt("stepn", stepn); | |
GPUMoveAgentsKernel(); | |
if (stepn % 2 == 1) | |
{ | |
GPUDiffuseTextureKernel(); | |
GPUWriteTrailsKernel(); | |
SwapTex(); | |
} | |
Render(); | |
} | |
private void GPUDiffuseTextureKernel() | |
{ | |
cs.SetTexture(diffuseTextureKernel, "readTex", readTex); | |
cs.SetTexture(diffuseTextureKernel, "writeTex", writeTex); | |
cs.SetFloat("trailDecayFactor", trailDecayFactor); | |
cs.Dispatch(diffuseTextureKernel, rez, rez, 1); | |
} | |
private void GPUMoveAgentsKernel() | |
{ | |
cs.SetBuffer(moveAgentsKernel, "agentsBuffer", agentsBuffer); | |
cs.SetTexture(moveAgentsKernel, "readTex", readTex); | |
cs.SetTexture(moveAgentsKernel, "debugTex", debugTex); | |
cs.Dispatch(moveAgentsKernel, agentsCount, 1, 1); | |
} | |
private void GPUWriteTrailsKernel() | |
{ | |
cs.SetBuffer(writeTrailsKernel, "agentsBuffer", agentsBuffer); | |
cs.SetTexture(writeTrailsKernel, "writeTex", writeTex); | |
cs.Dispatch(writeTrailsKernel, agentsCount, 1, 1); | |
} | |
private void SwapTex() | |
{ | |
RenderTexture tmp = readTex; | |
readTex = writeTex; | |
writeTex = tmp; | |
} | |
/* | |
* | |
* | |
* RENDER | |
* | |
* | |
*/ | |
private void Render() | |
{ | |
GPURenderKernel(); | |
GPUAgentsDebugKernel(); | |
outMat.SetTexture("_UnlitColorMap", outTex); | |
if (!Application.isPlaying) | |
{ | |
UnityEditor.SceneView.RepaintAll(); | |
} | |
} | |
private void GPURenderKernel() | |
{ | |
cs.SetTexture(renderKernel, "readTex", readTex); | |
cs.SetTexture(renderKernel, "outTex", outTex); | |
cs.SetTexture(renderKernel, "debugTex", debugTex); | |
cs.Dispatch(renderKernel, rez, rez, 1); | |
} | |
private void GPUAgentsDebugKernel() | |
{ | |
cs.SetBuffer(agentsDebugKernel, "agentsBuffer", agentsBuffer); | |
cs.SetTexture(agentsDebugKernel, "outTex", outTex); | |
cs.Dispatch(agentsDebugKernel, agentsCount, 1, 1); | |
} | |
/* | |
* | |
* | |
* UTIL | |
* | |
* | |
*/ | |
public void Release() | |
{ | |
if (buffers != null) | |
{ | |
foreach (ComputeBuffer buffer in buffers) | |
{ | |
if (buffer != null) | |
{ | |
buffer.Release(); | |
} | |
} | |
} | |
buffers = new List<ComputeBuffer>(); | |
if (textures != null) | |
{ | |
foreach (RenderTexture tex in textures) | |
{ | |
if (tex != null) | |
{ | |
tex.Release(); | |
} | |
} | |
} | |
textures = new List<RenderTexture>(); | |
} | |
private void onDestroy() | |
{ | |
Release(); | |
} | |
private void onEnable() | |
{ | |
Release(); | |
} | |
private void onDisable() | |
{ | |
Release(); | |
} | |
protected RenderTexture CreateTexture(int r, FilterMode filterMode) | |
{ | |
RenderTexture texture = new RenderTexture(r, r, 1, RenderTextureFormat.ARGBFloat); | |
texture.name = "out"; | |
texture.enableRandomWrite = true; | |
texture.dimension = UnityEngine.Rendering.TextureDimension.Tex2D; | |
texture.volumeDepth = 1; | |
texture.filterMode = filterMode; | |
texture.wrapMode = TextureWrapMode.Repeat; | |
texture.autoGenerateMips = false; | |
texture.useMipMap = false; | |
texture.Create(); | |
textures.Add(texture); | |
return texture; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment