How to ping pong a 3D texture?

I am coding a 3D cellular automata engine and need some help. I have used framebuffers in the past to create a 2D cellular automata, but I’m not sure if the same technique can be used here.

How would I read and write between two 3D textures?

Any help would be greatly appreciated!

I am not sure what the problem here if you need to ping pong a 3D texture simply create two textures then whilst you are reading from one texture you can be writing to the other, you swap between the textures by swapping which textures you bind as a uniform to the shader. Is there anything specific that you need help with? otherwise its very intuitive.

Here is an abstract process broken down.

Bind Texture 1 → Write Texture 2 → Bind Texture 2 → Write Texture 1…

EDIT: I see now that you actually need help with reading and writing to a texture.

To upload/write data to a texture you can use glTexSubImage3D. To read data from a texture you can use glGetTexImage.

For more information here are the functions from documentation which explains all parameters and usage etc…

glTexSubImage3D: glTexSubImage3D - OpenGL 4 - docs.gl
glGetTexImage: glGetTexImage - OpenGL 4 - docs.gl

I’m using an unsigned integer texture to store the cell states, how would I use glTexSubImage3D on that? Also would I have to keep a constantly updating array of states on the CPU side of things? Is there some way to use a shader to handle these textures? (Not to shift the goal posts on you)

I assume your thread here on 3D textures relates to your past thread here:

where you say:

On that link, Even Wallace says:

So this clarifies that by “ping pong”, you do mean the usual “ping pong rendering”, where you write to one texture while you read from another, …and then swap them for the next pass. For example:

  • Read A + Write B; then
  • Read B + Write A; then
  • (… repeat as needed…)

Rendering to textures is supported using Framebuffer Objects (FBOs).

So with ping-pong texture rendering using FBOs…:

  • The texture reads are implemented as texture sampling calls in the shader, and
  • The texture writes are implemented by:
    • Binding a 2D slice of a different texture to the COLOR_ATTACHMENT0 and/or DEPTH buffers of the FBO,
    • Binding that FBO as the active DRAW_FRAMEBUFFER, and then
    • Drawing using normal pipeline rasterization to this bound FBO, which writes to your 2D texture slice(s) bound to your FBO.

Normally this FBO “render-to-texture” rendering is performed with 2D or 2D multisample textures bound to the buffer(s) of the FBO. The only variation with your problem is that you have 3D textures, not 2D textures. So…

You have to bind 2D slices of a 3D texture to the FBO for drawing, not the whole 3D texture, and repeat your drawing once per 2D slice.

That is, apply this process iteratively for each slice of the 3D texture:

  • Bind slice 0 of the 3D texture to the FBO’s COLOR_ATTACHMENT0 buffer
  • Draw
  • Bind slice 1 of the 3D texture …
  • Draw

That’s probably what Even Wallace is referring to on that page when he says:

You have two basic options:

  1. Render a layer (slice) at a time using render-to-texture. Either bind a layer for each slice with glFramebufferTexture3D or glFramebufferTextureLayer, or bind the entire 3D texture as a layered framebuffer with glFramebufferTexture and select the layer by writing to gl_Layer in a geometry shader. The latter option allows the entire operation to be performed in a single draw call, but geometry shaders are slow on some architectures.

  2. Use a compute shader with image load/store. Requires OpenGL 4.3 or the ARB_compute_shader extension. Using render-to-texture for calculations (rather than rendering) was common before compute shaders were added.

Thanks. I managed to write a compute shader that loops over the texture and updates it accordingly. I am having some errors with it however, as the neighbor counting doesn’t appear to work.

for(int dx = -1; dx < 2; dx++)
{
	for(int dy = -1; dy < 2; dy++)
	{
		for(int dz = -1; dz < 2; dz++)
		{
			if(dx != 0 || dy != 0 || dz != 0)
			{
				int neighborState = int(texelFetch(imgInput, ivec3((x + dx), (y + dy), (z + dz)), 0).x);
			
				if(neighborState == uStates - 1)
				{
					neighborCount++;
				}
			}		
		}
	}
}

For some reason this neighbor checking code activates distant cells. And whenever cells get near the edges, they fill up that entire row or column. I’ve tried using the modulo operator, but it has seemingly no effect. (Also I’m aware this is slightly off topic, but I didn’t want to create a third topic just about my project, as that seems like it would be in poor taste)

The argument to texelFetch needs to lie within the bounds of the texture; out-of-range values aren’t wrapped or clamped.

There are a whole load of other things you could get wrong, but that’s the only one which relates to the posted code.

1 Like