Sending Textures to Fragment Shaders - solved

Started by Super_Llama, June 08, 2010, 23:47:10

Previous topic - Next topic

Super_Llama

I can't seem to find any tutorials for setting a uniform sampler2D inside a fragment shader program. I've done it with floats, and I've thoroughly searched ARBShaderObjects and GL11 for any relevant functions, but I haven't had any luck. The GLSL is easy, but I can't figure out how to create a texture, let alone apply it to the shader.

Any explanations would be great! I'm trying to make a 2D pixel-lit world with deformable terrain.

Matthias

You need to use glUniform1i and specify the texture unit (not the texture ID) starting from 0.
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(textureID);
glUniform1i(shaderUniformID, unit);

           

Super_Llama

Thanks, that explains part of it-- but I still need to know how to create a texture unit  :-\

Ciardhubh

Quote from: Super_Llama on June 09, 2010, 14:39:41
Thanks, that explains part of it-- but I still need to know how to create a texture unit  :-\

You don't create texture units, you just bind textures to the unit you want.
1. check how many the driver supports (http://www.opengl.org/sdk/docs/man/xhtml/glActiveTexture.xml, explanation under parameter description)
2. set active unit: glActiveTexture(GL_TEXTURE1)
3. bind texture
4. assign sampler2D variable a value of 1

Search google for "opengl multi-texturing" for more.

Super_Llama

The question is, how do you GET a texture to bind? That's what I meant by create texture unit-- I assumed it was similar to creating a shader program. I don't know how the java program stores the texture-- uploading it to the GPU once it's been created makes sense now though.

I need to know:
How to make an empty texture object in java and fill it with data
How to send this data to the fragment shader for use
How to modify the data once it's been sent, either inside the shader or by re-uploading chunks of data that have been modified
How to send more than one texture to the fragment shader at once (I'm planning on a color map for rendering, a stress map for cave-ins, a light map for enhancements, and a material map for various other calculations)

EDIT:
Quote from: Ciardhubh on June 09, 2010, 15:46:01
Search google for "opengl multi-texturing" for more.
Whoops, didn't see that. I'm doing that now, hopefully there will be enough information there to do what I want. Thanks :)

EDIT2:
Unfortunately that didn't help :|
All the tutorials I can find are for fixed-function multitexturing. I just need to know how to make a texture and send it to my fragment shader for sampling. Multiple texture coordinates aren't necessary since all the textures correspond to one another.

Ciardhubh

Works the same way you do it for fixed-function:
1. glGenTextures, to create tex objects
2. set active unit, to set the tex unit you want to bind to
3. bind, set texture you want to modify
4. set properties and glTexImage, to upload data to GPU
5. set sampler uniform, to map your uniform to that unit
6. use in GLSL

There's (almost) no difference between fixed-function and glsl in the way you set up textures. If you don't know how to do that I suggest reading general OpenGL tutorials (Google has dozens of them, Nehe's are quite popular) or read a more thorough book like the OpenGL Programming Guide (examples from that book in LWJGL are here: http://ciardhubh.de/node/13) or use an engine that does this for you (e.g. Slick, jME, jPCT, Ardor3D).

You modify textures by uploading different data via glTexImage. Textures are read-only in shaders.

You can use more textures by binding them to different texture units with glActiveTex...

If by "how the java program stores the texture" you mean loading image data from a file, then you need a loader for that image format. Existing engines like Slick or jME have those. That's not part of LWJGL.

Super_Llama

Quote from: Ciardhubh on June 09, 2010, 22:00:25If you don't know how to do that I suggest reading general OpenGL tutorials (Google has dozens of them, Nehe's are quite popular) or read a more thorough book like the OpenGL Programming Guide (examples from that book in LWJGL are here: http://ciardhubh.de/node/13)
Thanks, I'll look into those.
Quote from: Ciardhubh on June 09, 2010, 22:00:25
You modify textures by uploading different data via glTexImage. Textures are read-only in shaders.
Is there any way to upload a part of a large texture, or the entire texture relatively fast with no need to attempt optimization?
Quote from: Ciardhubh on June 09, 2010, 22:00:25
If by "how the java program stores the texture" you mean loading image data from a file, then you need a loader for that image format. Existing engines like Slick or jME have those.
Like I said in the last post, I'm not (for this specific shader at least) loading it from a file, but rather generating a starting image and is affected by various game events. I just need to know what the array of bytes that I upload to the GPU needs to look like :)

Ciardhubh

Quote from: Super_Llama on June 10, 2010, 03:20:16
Is there any way to upload a part of a large texture, or the entire texture relatively fast with no need to attempt optimization?
Something like http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage2D.xml ?

Quote from: Super_Llama on June 10, 2010, 03:20:16
I just need to know what the array of bytes that I upload to the GPU needs to look like :)

Image data is an array of bytes. OpenGL pixels starts at the bottom left corner of the image (as opposed to top left as in Java's BufferedImage for example). Pixels components you pass to OpenGL are usually defined as RGB or RGBA tuples (or any of the format types here: http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml). Whereas one component is for example of type  GL_UNSIGNED_BYTE (a value from 0 to 255) or 4 bytes for GL_FLOAT (0.0 to 1.0 IIRC).

LWJGL requires this array to be wrapped in a direct Buffer; use BufferUtils to create one.

Super_Llama

Quote from: Ciardhubh on June 10, 2010, 06:39:11
Something like http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage2D.xml ?
That's exactly the sort of thing I was looking for ;D
Quote from: Ciardhubh on June 10, 2010, 06:39:11
Image data is an array of bytes. OpenGL pixels starts at the bottom left corner of the image (as opposed to top left as in Java's BufferedImage for example). Pixels components you pass to OpenGL are usually defined as RGB or RGBA tuples (or any of the format types here: http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml). Whereas one component is for example of type  GL_UNSIGNED_BYTE (a value from 0 to 255) or 4 bytes for GL_FLOAT (0.0 to 1.0 IIRC).

LWJGL requires this array to be wrapped in a direct Buffer; use BufferUtils to create one.
Okay, that makes sense. Thanks :)

Let's see if I've got this right:

Use glGenTextures to create a texture object
Set the active unit and bind it
Set the image stuff like RGBA/GL_UNSIGNED_BYTE (not sure what function does this)
Use glTexImage or glTexSubImage2D to upload a texture or parts of it to the GPU (both contained in a ByteBuffer, read from left to right, bottom to top, 4 bytes per pixel)

Then, when rendering,

Set the active shader to the one you want
Activate/bind the texture to one of the GL_TEXTURE# registers
Set the uniform sampler2D to the texture unit (an int)

...and the GLSL does the rest?

I think I get it now, thanks for all the help. All that I need to know now is which function sets the image format for a texture unit before sending the data-- but I can probably find this by poking through the code completion menu and/or google.

So it looks like uniform sampler2D's are actually a sort of pointer in the GPU? Though they don't point to a memory location, they're still ints that point somewhere and have no other use in themselves. As long as I think about it that way, it makes perfect sense :)

Ciardhubh

Sounds about right. Don't forget to set tex coordinates and depending on your texture glTexParameter might be relevant too: http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml

Quote from: Super_Llama on June 10, 2010, 15:55:50
Set the image stuff like RGBA/GL_UNSIGNED_BYTE (not sure what function does this)

glTexImage... does that, e.g.:
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, texWidth, texHeight, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageBuffer);


Super_Llama

Having difficulty with the texture units...
GL11.glBindTexture has a 'target' parameter, apparently deciding whether to use a 1D, 2D, or Cube texture. GL13.glActiveTexture is apparently how I set the texture unit. I've been using GL_TEXTURE_2D for BindTexture and GL_TEXTURE1 for ActiveTexture, and sending '1' to the shader. I'm not sure what I'm doing wrong, but for some reason the shader is getting an empty texture (texture2D is always returning 0,0,0,0). I'm pretty sure my ByteByffer is right-- I'm creating it, putting values, and flipping it, so I'm thinking it's probably a problem with my understanding of texture units and texture targets.

From what I can see, it looks like each texture unit can hold one of each kind of texture, and that's what the targets are, but perhaps there's some incompatibility between the functions, using different models of texture storage?

The texture coordinates are being sent properly so that's not the problem.

Ciardhubh

Can you post your texture-relevant LWJGL and shader code?

Also shader units start at 0. Just saying so you don't waste one.

Try using the lwjgl-debug.jar instead of lwjgl.jar to see if any of the calls cause an OpenGL error.

Super_Llama

I used 0 initially but it didn't work so I tried 1. I'll switch back to 0 since that wasn't the problem.
This is the code that generates the texture (reddish noise):
       public void MakeTexture()
        {
            texrand = GL11.glGenTextures();
            GL13.glActiveTexture(GL13.GL_TEXTURE0);
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, texrand);
            ByteBuffer bb = BufferUtils.createByteBuffer(64*64*4);
            for (int i=0; i<4096; i++) {
                bb.put((byte)(155+Math.random()*100));
                bb.put((byte)(Math.random()*64));
                bb.put((byte)(Math.random()*128));
                bb.put((byte)255);
            }
            bb.flip();
            GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, 4, 64, 64, 0,
                              GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, bb);
        }

This is the code that binds it:
         GL13.glActiveTexture(GL13.GL_TEXTURE0);
          GL11.glBindTexture(GL11.GL_TEXTURE_2D, texrand);
          int loc = ARBShaderObjects.glGetUniformLocationARB(drawshad, "tex");
          ARBShaderObjects.glUniform1iARB(loc, 0);

And this is my test fragment shader:
uniform sampler2D tex;
uniform float sa; //sine of the current phase- just a test variable to make sure the two components can communicate

void main(void)
{
     vec4 color = texture2D(tex,gl_TexCoord[0].st);
     color.x *= sa; //multiply the red component by the animation variable
     color.w = 1; //make sure the alpha is 100%
     gl_FragColor = color;
}


EDIT: I changed to lwjgl-debug.jar and I'm getting spammed with an invalid enum error when setting the matrix. Not sure why, because that function worked just fine...

Ciardhubh

GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, bb);

If you don't need the alpha channel you can also drop it and only use GL11.GL_RGB to save memory.

Super_Llama

Quote from: Ciardhubh on June 10, 2010, 19:08:37
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, bb);

If you don't need the alpha channel you can also drop it and only use GL11.GL_RGB to save memory.

Didn't help, same problem. I read somewhere that passing '4' is the same as passing 'RGBA', but even after correcting it like you said, it still doesn't send the texture.

EDIT:
I added
if (color.y == 0) {
    color = vec4(1,0.8,0.5,1);
}

in the glsl, and the quad showed up-- so it looks like it's sending a ton of black pixels  ???