Compute shader isn't updating

I have two files (NewComputeShader.compute and ShaderRun.cs). ShaderRun.cs runs shader and draws it's texture on a camera (script is camera's component)

On start, unity draws one white pixel in a bottom-left corner. (Twidth = 256, Theight = 256, Agentsnum = 10)

NewComputeShader.compute:

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSUpdate

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Result;
uint width = 256;
uint height = 256;
int numAgents = 10;
float moveSpeed = 100;

uint PI = 3.1415926535;
float DeltaTime = 1;

uint hash(uint state) {
    state ^= 2747636419u;
    state *= 2654435769u;
    state ^= state >> 16;
    state *= 2654435769u;
    state ^= state >> 16;
    state *= 2654435769u;
    return state;
}

uint scaleToRange01(uint state) {
    state /= 4294967295.0;
    return state;
}

struct Agent {
    float2 position;
    float angle;
};

RWStructuredBuffer<Agent> agents;

[numthreads(8,8,1)]
void CSUpdate(uint3 id : SV_DispatchThreadID)
{
    //if (id.x >= numAgents) { return; }

    Agent agent = agents[id.x];
    uint random = hash(agent.position.y * width + agent.position.x + hash(id.x));

    float2 direction = float2(cos(agent.angle), sin(agent.angle));
    float2 newPos = agent.position + direction * moveSpeed * DeltaTime;

    if (newPos.x < 0 || newPos.x >= width || newPos.y < 0 || newPos.y >= height) {
        newPos.x = min(width - 0.01, max(0, newPos.x));
        newPos.y = min(height - 0.01, max(0, newPos.y));
        agents[id.x].angle = scaleToRange01(random) * 2 * PI;
    }

    agents[id.x].position = newPos;
    Result[int2(newPos.x, newPos.y)] = float4(1,1,1,1);
}

ShaderRun.cs:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShaderRun : MonoBehaviour
{

    public ComputeShader computeShader;
    public RenderTexture renderTexture;

    public int twidth;
    public int theight;
    public int agentsnum;

    ComputeBuffer agentsBuffer;

    struct MyAgent
    {
        public Vector2 position;
        public float angle;
    };

    // Start is called before the first frame update
    void Start()
    {
        renderTexture = new RenderTexture(twidth, theight, 24);
        renderTexture.enableRandomWrite = true;
        renderTexture.Create();

        computeShader.SetTexture(0, "Result", renderTexture);

        agentsBuffer = new ComputeBuffer(agentsnum, sizeof(float)*3); //make new compute buffer with specified size, and specified "stride"                                                                                                 //stride is like the size of each element, in your case it would be 3 floats, since Vector3 is 3 floats.
        ResetAgents();
        computeShader.SetBuffer(0, "agents", agentsBuffer); //Linking the compute shader and cs shader buffers

        computeShader.Dispatch(0, renderTexture.width / 8, renderTexture.height / 8, 1);
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Graphics.Blit(renderTexture, dest);
    }

    private void ResetAgents()
    {
        MyAgent[] aArray = new MyAgent[agentsnum];

        for (int i=0; i<agentsnum; i++)
        {
            MyAgent a = new MyAgent();
            a.position = new Vector2(128, 128);
            a.angle = 2 * (float)Math.PI * (i / agentsnum);
            aArray[i] = a;
        }
        agentsBuffer.SetData(aArray);
        ComputeStepFrame();
    }

    private void ComputeStepFrame()
    {
        computeShader.SetFloat("DeltaTime", Time.deltaTime);

        int kernelHandle = computeShader.FindKernel("CSUpdate");
        computeShader.SetBuffer(kernelHandle, "agents", agentsBuffer);
        computeShader.Dispatch(0, renderTexture.width / 8, renderTexture.height / 8, 1);
    }

    // Update is called once per frame
    void Update()
    {
        ComputeStepFrame();
    }
}

Also this is an attempt of recreating this code: https://www.youtube.com/watch?v=X-iSQQgOd1A&t=730s (part: Side-tracked by Slime). Result must be like on a first demonstration of agents in video. Edit: I really recommend to check this video. It is very good!

Upvotes: 0

Views: 1730

Answers (3)

Lis Glitchrain
Lis Glitchrain

Reputation: 41

Found this question after so much time. But theme is still interesting. Maybe my answer will help passersby here later. BTW look at this video. Slime is a game life form now!

The problem with the code from original question is ambiguity with what are you going to process with kernel.

[numthreads(8,8,1)]
void CSUpdate(uint3 id : SV_DispatchThreadID)
{
    //if (id.x >= numAgents) { return; }

    Agent agent = agents[id.x];
    //etc...
}

In this kernel you are intended to process 1D array of agents. You need to dispatch this code like this:

shader.Dispatch(kernelUpdateAgents, 
    Mathf.CeilToInt(numAgents / (float) xKernelThreadGroupSize), 
    1, 
    1);

And of course you need to correct kernel TGS:

[numthreads(8,1,1)] 
void CSUpdate(uint3 id : SV_DispatchThreadID)

For 2D texture you need to keep kernel like

[numthreads(8,8,1)]
void ProcessTexture(uint3 id : SV_DispatchThreadID)
{
//some work
}

And only then it is okay to dispatch it with second dimension:

shader.Dispatch(kernelProcessTexture, 
    Mathf.CeilToInt(TextureWidth / (float) xKernelThreadGroupSize), 
    Mathf.CeilToInt(TextureHeight / (float) yKernelThreadGroupSize), 
    1);

P.S. And there is a link to github repo under video.

Upvotes: 0

Andrew Collins
Andrew Collins

Reputation: 1

I am also attempting to recreate this. The problem is Sebastian leaves out his c# code and some of his HLSL so it's hard to put together the pieces that aren't there. I worked nonstop all day yesterday and finally got it to perform demonstration 2. The most difficult thing for me is getting the threading correctly and having the GPU compute all the items I need it to. I am dreading starting the trail dissipation and trail sense but honestly, it felt great getting to demo 2 and it's what is pushing me to keep at it. Everything is very touchy with this project and it is not for the casual programmer. (Also learn a bit about HLSL if you haven't.) Another thing is I don't use his random angle generator I just created my own. I know this doesn't help but just know other people are attempting to struggle through this also. Sebastian is a genius.

Upvotes: 0

Daniel Loughmiller
Daniel Loughmiller

Reputation: 11

I'm doing the same. To start the scaleToRange01 function should probably return a float. As for location you might want to look at the C# side, how are you initializing agents and getting that data into the buffer? Need to create a similar struct in C# then assign it something like below.

int totalSize = (sizeof(float) * 2) + (sizeof(float));
agentBuffer = new ComputeBuffer(agents.Length, totalSize);
agentBuffer.SetData(agents);
computeShader.SetBuffer(0, "agents", agentBuffer);

Upvotes: 1

Related Questions