#Creating a texture from one kernel, but then editing it from another

1 messages · Page 1 of 1 (latest)

round helm
#

I'm trying to recreate Sebastian Lague's slime simulation, but I'm stuck with one of the core concepts: Creating a texture from one kernel, but then editing it from another:.
Here's the interesting part of my code:
C#

// == Render everything == //
compute.Dispatch(_updateKernel, Mathf.CeilToInt(settings.numAgents / 64f), 1, 1);
compute.SetTexture(_updateKernel, "trail_map", trailMap);
Graphics.Blit(trailMap, renderTexture);
        
compute.SetTexture(_trailMapKernel, "to_diffuse_trail_map", renderTexture);
        
compute.Dispatch(_trailMapKernel, settings.width / 8, settings.height / 8, 1);


// == Update variables for next cycle == //
compute.SetTexture(_trailMapKernel, "to_process_trail_map", renderTexture);

Graphics.CopyTexture(trailMap, renderTexture);

Compute Shader

#pragma kernel Update
[numthreads(64,1,1)]
void Update (uint3 id : SV_DispatchThreadID)
{
    if (id.x >= num_agents) { return; }

    // *Removed* Declaring the Agent

    float2 new_pos = agent.position + direction * delta_time * agent_species.move_speed;

    // *Removed* Just some movement code
    
    agents[id.x].position = new_pos;
    
    trail_map[int2(new_pos.x, new_pos.y)] = 1;
}

#pragma kernel ProcessTrailMap

[numthreads(8,8,1)]
void ProcessTrailMap (uint3 id : SV_DispatchThreadID)
{
    const float4 original_value = to_process_trail_map[id.xy];
    const float4 evaporated_value = max(0, original_value - 1 * delta_time);

    processed_trail_map[id.xy] = evaporated_value;
    
}

What I'm trying to do is move some "agents" around a texture (Update), copy it, and then edit in another Kernel (ProcessTrailMap). However, if I use processed_trail_map[id.xy] = evaporated_value;, the texture is not edited. This changes if I do something like processed_trail_map[id.xy] = 0.5

ocean ridge
#

Why do you need to copy it?

round helm
#

I may be wrong, but I've found that textures are only contained within their kernels. ie the "trail_map" that is assigned to update cannot be read by the ProcessTrailMap kernel

ocean ridge
#

Textures are living in the GPU memory separate from shaders. When you set a texture, it simply passes the reference to that texture to a shader. Or in shader terms "it binds the texture resource to the shader".

#

So, you can set the same texture for several shaders/kernels.

round helm
#

But then why do you have to invoke a specific kernel when you want to read a texture?
compute.SetTexture(_trailMapKernel, "processed_trail_map", diffusedTrailMap);

#

Or am I misunderstanding

ocean ridge
#

That's binding the texture to the shader.

#

A kernel is a separate shader program.

#

Several kernels being in one text file doesn't make them the same shader.

#

It's like you have several C# classes in one file.

round helm
#

Then how would you read a texture from one kernel into another?

ocean ridge
#

You just set the same texture to both and they will access the same texture.

round helm
#

Could you give me an example? I don't fully understand the way that sharing textures between the CPU and GPU works.

ocean ridge
#

There's no sharing between CPU and GPU. Texture always live on the GPU. Sometimes you can have a copy of it on the CPU if you need to access it's data on the CPU.

round helm
#

ok

#

do you mean like this?
compute.SetTexture(_trailMapKernel, "processed_trail_map", diffusedTrailMap);
compute.SetTexture(_updateKernel, "processed_trail_map", diffusedTrailMap);

ocean ridge
#

Yes

round helm
#

ah ok

#

in that case, just to clarify, does editing processed_trail_map in _trailMapKernel also affect it in the _updateKernel?

ocean ridge
#

Btw, I'm not sure if I'm misunderstanding something, but it looks like you're setting the texture to a shader after dispatching it. That's not gonna do anything.

ocean ridge
round helm
round helm
round helm
ocean ridge
round helm
# ocean ridge I'm not sure how that's related. If you don't set the texture the shader doesn't...

I'll try break down how I understand that this whole thing worked, as well as my approach. Hopefully that will clarify something:

Let's start off with dispatching updateKernel to generate the positions of these agents. I then read the trail_map from the updateKernel, and copy it to renderTexture.

compute.SetTexture(_updateKernel, "trail_map", trailMap);```
This is because I want to use `renderTexture` to edit `trailMapKernel`'s model of the `trail_map`, which I named `to_process_trail_map`.
```Graphics.Blit(trailMap, renderTexture);```
The `trailMapKernel` then is supposed to process the `to_process_trail_map` and write that result to the `processed_trail_map`. 
```compute.Dispatch(_trailMapKernel, settings.width / 8, settings.height / 8, 1);```
I then read `processed_trail_map`, copying it to C#'s `trailMap` (I seem to have made an error in my original message, which I'll edit).
```compute.SetTexture(_trailMapKernel, "to_process_trail_map", renderTexture [actually trailMap]);```
I copy `trailMap` to `renderTexture`, which is then rendered to the camera.
```Graphics.CopyTexture(trailMap, renderTexture);```
ocean ridge
round helm
#

I've tried editing it to follow what I understand from what you've stated, but the ProcessTrailMap does not seem to be using the trail_map variable, which was initialised here:

        compute.SetTexture(_updateKernel, "trail_map", trailMap);
        compute.SetTexture(_trailMapKernel, "trail_map", trailMap);

When using this code:

        // == Render everything == //
        compute.Dispatch(_updateKernel, Mathf.CeilToInt(settings.numAgents / 64f), 1, 1);
        compute.SetTexture(_updateKernel, "trail_map", trailMap);
        
        
        compute.Dispatch(_trailMapKernel, settings.width / 8, settings.height / 8, 1);


        // == Update variables == //
        compute.SetTexture(_trailMapKernel, "trail_map", renderTexture);

I'm using the SetTexture to debug trailMap, btw.
However, if I change this line: compute.SetTexture(_trailMapKernel, "trail_map", renderTexture); to compute.SetTexture(_updateKernel, "trail_map", renderTexture);, the map renders, although it is still not diffusing.
Here's the update shader code, btw:


[numthreads(8,8,1)]
void ProcessTrailMap (uint3 id : SV_DispatchThreadID)
{
    if (id.x < 0 || id.x >= (uint)width || id.y < 0 || id.y >= (uint)height) { return; }
    const float4 original_value = trail_map[id.xy];
    const float4 evaporated_value = max(0, original_value - 0.5);

    trail_map[id.xy] = evaporated_value;
}

Is this what you meant?

ocean ridge
#

I mean that all of the "set"s of a shader/kernel need to come before you dispatch it.

#

There no point setting them afterwards as the shader would start or even complete it's job by then.

round helm
#

But if I'm reading the documentation correctly, using compute.SetTexture with a RenderTexture with enableRandomWrite, that reads the value from the compute shader, not writes.

I'm running this simulation in FixedUpdate btw.

ocean ridge
round helm
#

"This function can either set a regular texture that is read in the compute shader, or an output texture that is written into by the shader. For an output texture, it has to be a RenderTexture with random write flag enabled, see RenderTexture.enableRandomWrite."

ocean ridge
#

Setting the properties after executing you shader is like doing a + b, getting the result, then saying "oh I want a to be 4, so I set it to 4" now. But the function is already executed with the previous values.

round helm
#

I'm not editing the variables though, I'm using them to render the scene

ocean ridge
#

Yes, You can Set input or output textures, but it needs to be done before the shader is executed

round helm
#

one moment

round helm
ocean ridge
#

No. You can't read anything afterwards. Or rather, there's nothing to read from, because the shader would be writing into empty space.

#

When you set a texture, you're telling this shader "when you execute, use this texture for input/output"

round helm
#

tysm.