Learning Compute shaders in tsl

From where can I know more about compute shaders in tsl.

and one more thing, when i do compute shaders in tsl with webgpuRenderer, will it work inside webgl?

There are no compute shaders in WebGL, so it won’t really be the same. However it’s possible that three does some kind of a fallback.

Type the search term “compute” in the three.js examples and you should find several useful examples. Note that they all appear in the WebGPU section.

I can’t imagine how three.js could make compute shaders work in WebGL. For me, the ability to use compute shaders is a big reason why I switched to using WebGPU.

However, from what I have seen compute shaders do not work well (at all?) with iPhone or iPads.

There has been a concept of gpgpu with WebGL. I think you overall have less control, and it’s not as efficient but can essentially do the same thing. Regarding mobile, I has some issues until recently just running WebGPU on my iPhone.

I can get at least some of the three.js WebGPU examples to run on my iPhone.
But I can’t get some of my WebGPU programs to run on my iPhone.
Is there a special switch to set? Or CSS instruction?

I don’t know much about this, perhaps :confused:

Now that I look at the three.js WebGPU examples, I see that they have changed the stylesheet from main.css to example.css. The main.css included the following instructions in the info section:

	-moz-user-select: none;
	-webkit-user-select: none; // relates to Apple ios
	-ms-user-select: none;

The example.css has none of these instructions.
Nor do the program headers appear to include any Apple ios- related instructions.

I believe that one advantage of using tsl is that it works well with Apple ios.

I think you might be mixing up a few things here. CSS is hardly related to WebGL/webgpu. While you do have to use it to control the sizing of the canvas element, in that context the canvas is just a dom element like any other. You don’t have to ask for the WebGL context, you can ask for the 2d context. As such it has nothing to do with shaders and by extension TSL.

I don’t think there is a CSS setting that is related to the availability of WebGPU. MDN has a good reference for all of these. What you’re looking at prevents the user to select the text within that div.

I am unable to run WebGPU on my iPhone, probably need to do some update.

What I need is the texture output from the compute shaders. Do you guys know any way?

1 Like

Like this example? And this example puts animated textures on spheres. Several three.js versions ago, I modified the last example to work on a flat plane.

I’m afraid we’ve gone off on a tangent.

You had said “Regarding mobile, I has some issues until recently just running WebGPU on my iPhone.” I thought that meant that you had might have found a special way to turn on WebGPU on your iPhone.

On my iPhone, WebGPU is working automatically. But there was one WebGPU program in my collection that didn’t work. So I was hoping that problem might be solved if there was a special switch or special commands on my iPhone or in my program or css that could make it work.

I agree that the css change would not change things. Older css instructions used to have several lines relating to the Apple webkit. Those are all gone.

i also need access to the UV coordinates, but I don’t know how to get them.

in the code I can see


const posX = instanceIndex.mod(width);
const posY = instanceIndex.div(width);
const indexUV = uvec2(posX, posY);

but its not the UV coordinate :smiling_face_with_tear:

or maybe I’m missing something?

got it, I just need to normalize it, and that’s it.

const uv = vec2(
   float(posX).add(0.5).div(width),
   float(posY).add(0.5).div(height)
);
1 Like

Just to make sure because you started the discussion talking about compute shaders and ended up talking about textures (fragment shaders). In WGSL, you use different commands to save your final values. For fragment shaders, you use TextureStore(destination,index,vec4(value). For compute shaders, you simply use Destination(index). As I recall, the index for compute shaders does not use uv values.

Here is a program where I made compute shader results visible by transferring the contents of those compute shaders to fragment shaders. (This is a series of shaders that compute vertex values for ocean waves.) This is helpful if you want to see what is going on. But, as the first panel shows, the results are not always visible. And you always need to set the last value (the “a” in rgba) = 1.

1 Like

Here’s one demo I created, using compute shaders for textures.

Is there any way to use a single StorageTexture instead of using 2 as ping pong buffer. Like how we do it with instancedArray?

@Mugen87 @sunag

@dubois @phil_crowther

Sorry, that’s not an area that I have much experience with. I would be interested in hearing the answer though.

@gobinda-das-dev

A few things:

Conceptually, there shouldn’t be anything preventing you from using your readTexture as your writeTexture. After all, each compute invocation only reads and writes to the texel specified by the instanceIndex, so there’s no risk of one invocation reading from a texel that has already been written to by another.

However, the main problem here is that you’re not explicitly using storage textures within your shaders. Rather, you’re defining Storage Textures outside of the shader but passing them in as regular textures. Accordingly, the WGSL output for your shader looks like this

// ...etc, Three.js shader nonsense
// Equivalent of your writeTexture. It only gets interpreted as a storage texture because textureStore requires a texture_storage_2d variable as an argument.
@bindingbindin@group( 0 ) @group( 0 ) var nodeUniform0 : texture_storage_2d<rgba8unorm, write>;

// Equivalent of your readTexture. TextureLoad can natively read regular textures. Accordingly, even though you are passing it a StorageTexture class, the shader feels no need to properly convert it to a stora@bindinge text@groupre.
@binding( 1 ) @group( 0 ) var nodeUnif@bindingrm1_sa@grouppler : sampler;
@binding( 2 ) @group( 0 ) var nodeUniform1 : texture_2d;

// More Three.js shader nonsense

nodeVar0 = vec2( ( instanceIndex % 32u ), ( instanceIndex / 32u ) );
nodeVar1 = nodeVar0;
nodeVar2 = textureLoad( nodeUniform1, vec2( nodeVar0 ), u32( 0u ) );
nodeVar3 = ( 1.0 - step( 0.025, length( ( ( ( vec2( nodeVar1 ) + vec2( 0.5 ) ) / vec2( 32.0, 32.0 ) ) - object.nodeUniform2 ) ) ) );
textureStore( nodeUniform0, nodeVar0, ( ( nodeVar2 \* vec4( 0.98 ) ) + vec4( nodeVar3, nodeVar3, nodeVar3, 1.0 ) ) );

}

The solution to properly using a single StorageTexture as a read-write target can be found here: three.js/examples/webgpu_compute_texture_pingpong.html at master · mrdoob/three.js · GitHub. Notice how the example both creates a StorageTexture class and passes that StorageTexture to the storageTexture node so it gets properly interpreted within the shader. Accordingly, to use a single storageTexture, just pass your texture to a storageTexture node, set its access to NodeAccess.READ_WRITE, and replace your two textures with the single storageTexture node.

Depending on the format of your texture, you might still run into restrictions regarding what kinds of textures can be read/written to so let the chat know if that happens.

TLDR Do this:

const currentTexture = new THREE.StorageTexture( size, size );
const currentTextureNode = storageTexture( currentTexture ).setAccess( NodeAccess.READ_WRITE );

Then replace readTexture and writeTexture with the currentTexture. Again, this will only work your provided example because each compute invocation is reading from the same texel defined by its instanceIndex.

3 Likes

Wow, thank you so much, man; it works :folded_hands:.

For future reference, here’s the updated fiddle.

1 Like

No problem :slightly_smiling_face:

For future reference, if you end up having trouble with your shaders in the future, I would recommend using WebGPU Inspector and looking at the Shader Modules. Sometimes TSL doesn’t translate into shader code exactly as we might expect it to (for instance, the select node does not natively translate into a select. It only translates into an If/Else statement unless .uniformFlow is added)

1 Like