Mipmapped array textures?

Started by ent_erthevoid, January 09, 2015, 02:21:41

Previous topic - Next topic

ent_erthevoid

Hi guys,

I recently switched to using an array texture to load all my textures into the one array...

Before, I was passing each texture (diffuse, normal, displacement) to separate samplers in my shader, and I had mip maps enabled. The thing is, since switching to using an array texture, mip mapping no longer works. This is my code:

   
// I pass in 3 strings to this method, which are the 3 texture names
    private static int loadPNGTexture(String ... filenames) {
		ByteBuffer buf = null;
		int tWidth = 1024;
		int tHeight = 1024;

		int texId = GL11.glGenTextures();
		GL11.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, texId);
		GL42.glTexStorage3D(GL30.GL_TEXTURE_2D_ARRAY, 1, GL11.GL_RGBA8, 1024, 1024, 3); //last 3 params are image size and array size

 		for (int i = 0; i < filenames.length; i++){
			try {
				 // Open the PNG file as an InputStream
				InputStream in = new FileInputStream(filenames[i]);
				 // Link the PNG decoder to this stream
				PNGDecoder decoder = new PNGDecoder(in);

				// Get the width and height of the texture
				tWidth = decoder.getWidth();
				tHeight = decoder.getHeight();

				// Decode the PNG file in a ByteBuffer
				buf = ByteBuffer.allocateDirect(4 * decoder.getWidth() * decoder.getHeight());
				decoder.decode(buf, decoder.getWidth() * 4, Format.RGBA);
				buf.flip();

				in.close();
				
		                GL12.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, tWidth, tHeight, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf);
				
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(-1);
			}
		}
		
		return texId;
	}


I cleaned out the old mip mapping code, since I don't think it will apply to a 2D texture array...how do I modify the above code to have mip mapping working on all 3 textures?

Thanks

Kai

Hey,

you do it with 2D texture arrays (using glTexStorage3D, as you do) the same way that you would with single 2D textures (using glTexStorage2D).
That is, you must allocate the number of mip levels that you want.
Currently, you are allocating only the first level of your texture (second argument to glTexStorage3D).
Then, you would (as usual) glGenerateMipmap.

Another thing:
You surely do this, but, you know, for texture arrays I think you need to use shaders to access the individual textures.
There might be some twisted OpenGL extension that allows assigning an array layer to individual vertices, but usually you would do it in a fragment shader with 'sampler2DArray' and 'texture2DArray' function and mipmapping filtering activated for your sampler.
But you surely do this already. :)

ent_erthevoid

okay, so i updated my code my second parameter for glTexStorage3D to be "3" (line 9), and later on, i call glGenerateMipmap(line 37):

private static int loadPNGTexture(String ... filenames) {
		ByteBuffer buf = null;
		int tWidth = 1024;
		int tHeight = 1024;

		// Create a new texture object in memory and bind it
		int texId = GL11.glGenTextures();
		GL11.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, texId);
		GL42.glTexStorage3D(GL30.GL_TEXTURE_2D_ARRAY, 3, GL11.GL_RGBA8, 1024, 1024, 3); //last 3 params are image size and array size

		for (int i = 0; i < filenames.length; i++){
			try {
				// Open the PNG file as an InputStream
				InputStream in = new FileInputStream(filenames[i]);
				// Link the PNG decoder to this stream
				PNGDecoder decoder = new PNGDecoder(in);

				// Get the width and height of the texture
				tWidth = decoder.getWidth();
				tHeight = decoder.getHeight();

				// Decode the PNG file in a ByteBuffer
				buf = ByteBuffer.allocateDirect(4 * decoder.getWidth() * decoder.getHeight());
				decoder.decode(buf, decoder.getWidth() * 4, Format.RGBA);
				buf.flip();

				in.close();
				
				GL12.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, tWidth, tHeight, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf);
				
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(-1);
			}
		}
		
		GL30.glGenerateMipmap(GL30.GL_TEXTURE_2D_ARRAY);

		// Setup the ST coordinate system
		GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
		GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);

		// Setup what to do when the texture has to be scaled
		GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
		GL11.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
		
		return texId;
}


however, when i zoom out away from my textures, the mip maps aren't being displayed

this page: https://www.opengl.org/wiki/Array_Texture talks about using glTexImage3D, but i've no idea how to use it to generate mipmaps - i've tried using it, but i got a bunch of errors about my buffer size, and invalid operations when i did

does anyone know how to use glTexImage3D properly? this is how my textures are looking now:



and this is how they *should* look with mipmapping enabled:



any help on the matter is greatly appreciated! :)

Kai

After you glTexStorage3D your texture and loaded the first mipmap level into it via glTexSubImage3D (as you do), you need to glGenerateMipmap.
Then you need to enable mipmap filtering for your sampler/texture with glTexParameter and GL_TEXTURE_MIN_FILTER and GL_LINEAR_MIPMAP_LINEAR (for example).
Then you need to use the mentioned shader function to access the wanted array layer in your shader, to which mipmapping is then applied.
Using glTexImage3D makes no difference for you in your case. That function is about allocating the n-th texture mipmap level and also filling it with defined texels. Think of it as the aggregation of glTexStorage3D and glTexSubImage3D.

ent_erthevoid

but i'm already doing that?

i generate mip maps on line 37

i then set the filters on lines 40-45

so what am i missing/doing wrong?

Kai

Please let us have a look at your fragment shader code. Maybe the error is there.

ent_erthevoid

the shader code is fine, i'm selecting all the right texture, including their offsets - hence why i'm able to get normal and displacement maps working

the issue is generating my mipmaps. this website: https://www.opengl.org/wiki/Array_Texture says i need to use glTexImage3D, which im currently not doing, because i don't know how

Kai

Because I was rather curious why it does not work for you, I have created in the LWJGL3 Github repository a demo application for mipmapping with texture2D arrays.

Please have a look at: https://github.com/LWJGL/lwjgl3/blob/master/src/tests/org/lwjgl/demo/opengl/textures/Texture2DArrayMipmapping.java

For me it works perfectly with glTexStorage3D and glTexSubImage3D.
Although one could add some anisotrophic filtering as a sampler parameter there.

ent_erthevoid

thank you very much for taking the time to do that

i see you are requesting gl version 4.2 - i am requesting gl 3.2. if i change it to 4.2, then my compilation fails at my call to GL30.glGenVertexArrays() with the following:

Exception in thread "main" java.lang.IllegalStateException: Function is not supported
   at org.lwjgl.BufferChecks.checkFunctionAddress(BufferChecks.java:58)
   at org.lwjgl.opengl.GL30.glGenVertexArrays(GL30.java:1633)

I'm assuming it's because OSX doesn't support gl 4.2?

Kai

Hm... then I am all the more curious why that glTexStorage3D actually succeeded for you, as that is only available on GL >= 4.2. :)
But, I will change the example to use glTexImage3D then.

EDIT: Okay, demo changed to GL33. And works for me.
EDIT2 (19:14 CET): Turns out that we do not have to know the number of mipmap levels when using glTexImage3D instead of glTexStorage3D as that glGenerateMipmaps will allocate that for us with mutable textures - so example modified again