Texture Sampler IDs not mapping to Texture Units - opengl

I'm attempting to use 3 textures on a model. I'm loading the images using SOIL and binding them to texture units 1, 2, and 3. But for some reason when I bind samplers 1, 2, and 3 to my shader, 1 produces the image bound to GL_TEXTURE2 while 2 and 3 produce the image bound to GL_TEXTURE3. Am I missing something simple here?
Definitions:
GLenum baseTextureUnitID = GL_TEXTURE1;
GLenum filterTextureUnitID = GL_TEXTURE2;
GLenum accentTextureUnitID = GL_TEXTURE3;
GLuint baseTextureSamplerValue = 1;
GLuint filterTextureSamplerValue = 2;
GLuint accentTextureSamplerValue = 3;
Loading Images:
baseTextureID = SOIL_load_OGL_texture(baseImageFile,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
glActiveTexture(baseTextureUnitID);
glBindTexture(GL_TEXTURE_2D, baseTextureID);
filterTextureID = SOIL_load_OGL_texture(filterImageFile,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
glActiveTexture(filterTextureUnitID);
glBindTexture(GL_TEXTURE_2D, filterTextureID);
accentTextureID = SOIL_load_OGL_texture(accentImageFile,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
glActiveTexture(accentTextureUnitID);
glBindTexture(GL_TEXTURE_2D, accentTextureID);
Getting uniform locations:
baseTextureSamplerID = glGetUniformLocation(shaderProgram, "baseTex");
filterTextureSamplerID = glGetUniformLocation(shaderProgram, "filterTex");
accentTextureSamplerID = glGetUniformLocation(shaderProgram, "accentTex");
Setting Uniforms:
glUniform1i(baseTextureSamplerID, baseTextureSamplerValue);
glUniform1i(filterTextureSamplerID, filterTextureSamplerValue);
glUniform1i(accentTextureSamplerID, accentTextureSamplerValue);
Now if I sample from baseTex I get the filter Image, while the filterTex and accentTex samplers result in the accent image

Your problem is with the order of calls when you load the textures. SOIL_load_OGL_texture() will change your current texture bindings. It has to, since it needs to bind the newly created texture so that it can load data into it.
Just picture what happens in this sequence of your code:
glActiveTexture(baseTextureUnitID);
glBindTexture(GL_TEXTURE_2D, baseTextureID);
At this point, baseTextureID is bound to the intended texture unit. But the next line is:
filterTextureID = SOIL_load_OGL_texture(filterImageFile, ...);
Inside this call, the newly created texture will be bound to the current texture unit, which is the one you were planning to use for the base texture. This is the reason you have the wrong textures bound at the end of loading all the textures.
Fixing this is easy. Simply change the order of the calls in your image loading code to first load all the textures, and then establish all the bindings:
baseTextureID = SOIL_load_OGL_texture(baseImageFile,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
filterTextureID = SOIL_load_OGL_texture(filterImageFile,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
accentTextureID = SOIL_load_OGL_texture(accentImageFile,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
glActiveTexture(baseTextureUnitID);
glBindTexture(GL_TEXTURE_2D, baseTextureID);
glActiveTexture(filterTextureUnitID);
glBindTexture(GL_TEXTURE_2D, filterTextureID);
glActiveTexture(accentTextureUnitID);
glBindTexture(GL_TEXTURE_2D, accentTextureID);

I've found the solution to this issue. When using multiple textures as I am, you have to activate the texture unit, and bind the texture again before calling glUniform1i for the sampler.
When setting uniforms, the correct format is:
glActiveTexture(baseTextureUnitID);
glBindTexture(GL_TEXTURE_2D, baseTextureID);
glUniform1i(baseTextureSamplerID, baseTextureSamplerValue);
glActiveTexture(filterTextureUnitID);
glBindTexture(GL_TEXTURE_2D, filterTextureID);
glUniform1i(filterTextureSamplerID, filterTextureSamplerValue);
glActiveTexture(accentTextureUnitID);
glBindTexture(GL_TEXTURE_2D, accentTextureID);
glUniform1i(accentTextureSamplerID, accentTextureSamplerValue);

Related

OpenGL - blend two textures on the same object

I want to apply two textures on the same object (actually just a 2D rectangle) in order to blend them. I thought I would achieve that by simply calling glDrawElements with the first texture, then binding the other texture and calling glDrawElements a second time. Like this:
//create vertex buffer, frame buffer, depth buffer, texture sampler, build and bind model
//...
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
glBlendEquation(GL_FUNC_ADD);
// Clear the screen
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Bind our texture in Texture Unit 0
GLuint textureID;
//create or load texture
//...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);
// Set our sampler to use Texture Unit 0
glUniform1i(textureSampler, 0);
// Draw the triangles !
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)0);
//second draw call
GLuint textureID2;
//create or load texture
//...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID2);
// Set our sampler to use Texture Unit 0
glUniform1i(textureSampler, 0);
// Draw the triangles !
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)0);
Unfortunately, the 2nd texture is not drawn at all and I only see the first texture. If I call glClear between the two draw calls, it correctly draws the 2nd texture.
Any pointers? How can I force OpenGL to draw on the second call?
As an alternative to the approach you followed so far I would like to suggest using two texture samplers within your GLSL shader and perform the blending there. This way, you would be done with just one draw call, thus reducing CPU/GPU interaction. To do so, just define to texture samplers in your shader like
layout(binding = 0) uniform sampler2D texture_0;
layout(binding = 1) uniform sampler2D texture_1;
Alternatively, you can use a sampler array:
layout(binding = 0) uniform sampler2DArray textures;
In your application, setup the textures and samplers using
enum Sampler_Unit{BASE_COLOR_S = GL_TEXTURE0 + 0, NORMAL_S = GL_TEXTURE0 + 2};
glActiveTexture(Sampler_Unit::BASE_COLOR_S);
glBindTexture(GL_TEXTURE_2D, textureBuffer1);
glTexStorage2D( ....)
glActiveTexture(Sampler_Unit::NORMAL_S);
glBindTexture(GL_TEXTURE_2D, textureBuffer2);
glTexStorage2D( ....)
Thanks to #tkausl for the tip.
I had depth testing enabled during the initialization phase.
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
The option needs to be disabled in my case, for the blend operation to work.
//make sure to disable depth test
glDisable(GL_DEPTH_TEST);

OpenGL multiple texture with multiple shader programs

I am trying to do a scene in OpenGL to simulate earth from space. I have two spheres right now, one for earth, and another slightly big for clouds. The earth and the cloud sphere objects have their own shader programs to keep it simple. The earth shader program takes 4 textures (day, night, specmap and normalmap) and the cloud shader program takes 2 textures (cloudmap and normalmap). I have an object class which has a render function, and in that function I use this logic:
//bind the current object's texture
for (GLuint i = 0; i < texIDs.size(); i++){
glActiveTexture(GL_TEXTURE0 + i);
if (cubemap)
glBindTexture(GL_TEXTURE_CUBE_MAP, texIDs[i]);
else
glBindTexture(GL_TEXTURE_2D, texIDs[i]);
}
if (samplers.size()){
for (GLuint i = 0; i < samplers.size(); i++){
glUniform1i(glGetUniformLocation(program, samplers[i]), i);
}
}
It starts from the 0th texture unit, and binds N number of textures to N number of texture units starting from GL_TEXTURE0. Then it binds the the samplers starting from 0 to N in the shader program. The samplers are provided by me while loading the textures:
void Object::loadTexture(const char* filename, const GLchar* sampler){
int texID;
texID = SOIL_load_OGL_texture(filename, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS);
if(texID == 0){
cerr << "SOIL error: " << SOIL_last_result();
}
cout << filename << " Tex ID: " << texID << endl;
texIDs.push_back(texID);
samplers.push_back(sampler);
//glBindTexture(GL_TEXTURE_2D, texID);
}
When I do this, all the textures in the first sphere (earth) gets loaded successfully, but in the seconds sphere I get no textures and I just get a black sphere. My query is, how should I manage multiple textures and samplers if I'm using different shader programs for each object?
From what I see You are binding all textures as separate texture unit
that is wrong
what if you have 100 objects and each has 4 textures ...
I strongly doubt that you have 400 texture units at your disposal
Texture ID (name) is not Texture unit ...
I render space bodies like this:
First pass renders the astro body geometry
I have specific texture units for specific tasks
// texture units:
// 0 - texture0 map 2D rgba (surface)
// 1 - texture1 map 2D rgba (clouds blend)
// 2 - normal map 2D xyz (normal/bump mapping)
// 3 - specular map 2D i (reflection shininess)
// 4 - light map 2D rgb rgb (night lights)
// 5 - enviroment/skybox cube map 3D rgb
see the shader in that link (it was written for the solar system visualization too)...
you bind only the textures for single body before each render of it
(after you bind the shader)
do not change the texture unit meanings (how shader will know which texture is what if you do?)
Second render pass adds atmospheres
no textures used
it is just single transparent quad covering whole screen
here some insights to your tasks
[edit1] example of multitexturing
// init shader once per render all geometries
GLint prog_id; // shader program ID;
GLint txrskybox; // global skybox environment cube map
GLint id;
glUseProgram(prog_id);
id=glGetUniformLocation(prog_id,"txr_texture0"); glUniform1i(id,0); //uniform sampler2D txr_texture0;
id=glGetUniformLocation(prog_id,"txr_texture1"); glUniform1i(id,1); //uniform sampler2D txr_texture1;
id=glGetUniformLocation(prog_id,"txr_normal"); glUniform1i(id,2); //uniform sampler2D txr_normal;
id=glGetUniformLocation(prog_id,"txr_specular"); glUniform1i(id,3); //uniform sampler2D txr_specular;
id=glGetUniformLocation(prog_id,"txr_light"); glUniform1i(id,4); //uniform sampler2D txr_light;
id=glGetUniformLocation(prog_id,"txr_skybox"); glUniform1i(id,5); //uniform samplerCube txr_skybox;
// add here all uniforms you need ...
glActiveTexture(GL_TEXTURE0+5); glEnable(GL_TEXTURE_CUBE_MAP); glBindTexture(GL_TEXTURE_CUBE_MAP,txrskybox);
for (i=0;i<all_objects;i++)
{
// add here all uniforms you need ...
// pass textures once per any object render
// obj::(GLint) txr0,txr1,txrnor,txrspec,txrlight; // object local textures
glActiveTexture(GL_TEXTURE0+0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,obj[i].txr0);
glActiveTexture(GL_TEXTURE0+1); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,obj[i].txr1);
glActiveTexture(GL_TEXTURE0+2); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,obj[i].txrnor);
glActiveTexture(GL_TEXTURE0+3); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,obj[i].txrspec);
glActiveTexture(GL_TEXTURE0+4); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,obj[i].txrlight);
// here render the geometry of obj[i]
}
// unbind textures and shaders
glActiveTexture(GL_TEXTURE0+5); glBindTexture(GL_TEXTURE_CUBE_MAP,0); glDisable(GL_TEXTURE_CUBE_MAP);
glActiveTexture(GL_TEXTURE0+4); glBindTexture(GL_TEXTURE_2D,0); glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0+3); glBindTexture(GL_TEXTURE_2D,0); glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0+2); glBindTexture(GL_TEXTURE_2D,0); glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0+1); glBindTexture(GL_TEXTURE_2D,0); glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0+0); glBindTexture(GL_TEXTURE_2D,0); glDisable(GL_TEXTURE_2D); // unit0 at last so it stays active ...
glUseProgram(0);

switching between textures loaded with SOIL

I'm attempting to load two textures and then switch between the two in my display function. I am using the SOIL library to load the textures as such:
tex_2 = SOIL_load_OGL_texture
(
"s9.png",
SOIL_LOAD_AUTO,
SOIL_CREATE_NEW_ID,
SOIL_FLAG_MIPMAPS | SOIL_FLAG_COMPRESS_TO_DXT
);
tex_1 = SOIL_load_OGL_texture
(
"s8.png",
SOIL_LOAD_AUTO,
SOIL_CREATE_NEW_ID,
SOIL_FLAG_MIPMAPS | SOIL_FLAG_COMPRESS_TO_DXT
);
And then I use
glBindTexture(GL_TEXTURE_2D, tex_1)
or
glBindTexture(GL_TEXTURE_2D, tex_2);
To switch between the two. The problem is I must be loading them incorrectly and I'm not sure how. Whichever texture I load in last (tex_1 in the code above) is the the texture that I get for both tex_1 and tex_2 when I try to switch with glBindTexture. Any ideas?
Before loading the teaxtures I set up blending and turn on texture and sprites
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE);
glEnable(GL_TEXTURE_2D);
glEnable(GL_POINT_SPRITE);
glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
Then in my display function, I loop over all my points which I want to draw and attempt to change the current texture before drawing the point with glVertex3f:
for(int i=0; i<num_particles; i++)
{
//select texture to use
if(class[i] == 2.0f)
glBindTexture(GL_TEXTURE_2D, tex_2);
else
glBindTexture(GL_TEXTURE_2D, tex_1);
glVertex3f(posn[3*i], posn[3*i+1], posn[3*i+2]);
}
The goal is I have two types of points and the sprite to be drawn at each point depends on the class the point is in
for(int i=0; i<num_particles; i++)
{
//select texture to use
if(class[i] == 2.0f)
glBindTexture(GL_TEXTURE_2D, tex_2);
else
glBindTexture(GL_TEXTURE_2D, tex_1);
glVertex3f(posn[3*i], posn[3*i+1], posn[3*i+2]);
}
You can't call glBindTexture() inside a glBegin()/glEnd() pair:
GL_INVALID_OPERATION is generated if glBindTexture is executed between the execution of glBegin and the corresponding execution of glEnd.
The last successful glBindTexture() was probably in the most recent SOIL_load_OGL_texture() call. That's why tex_1 and tex_2 seem to contain the same texture data: tex_2 is never rebound.
As previous answers: you cannot change textures between glBegin/glEnd.
What can you do then?
sort your particles by material... in this case by your texture. Set the first texture, draw particles (glBegin/glEnd), then set the second texture and draw remaining particles.
use texture atlases - http://en.wikipedia.org/wiki/Texture_atlas. Here is some more advanced usage: http://www.popcornfx.com/wiki/index.php/Particle_tutorial_smoke
that way you will have one texture, but you will have to set different texture coordinates...

using glTexture in a shader

I'm using openGL and what I want to do is render my scene to a texture and then store that texture so that I can pass it into my fragment shader to be used to render something else.
I created a texture using glGenTexture() and attached it to a frame buffer and then rendered the frame buffer with glBindFrameBuffer(). I then set the framebuffer back to 0 to render back to the screen but now I'm stuck.
In my fragment shader I have a uniform sampler 2D 'myTexture' that I want to use to store the texture. How do I go about doing this?
For .jpg/png images that I found online I just used the following code:
glUseProgram(Shader->GetProgramID());
GLint ID = glGetUniformLocation(
Shader->GetProgramID(), "Map");
glUniform1i(ID, 0);
glActiveTexture(GL_TEXTURE0);
MapImage->Bind();
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
However this code doesn't work for the glTexture I created. Specifically I can't call myTexture->Bind() in the same way.
If you truly have "a uniform sampler 2D 'myTexture'" in your shader, why are you getting the uniform location for "Map"? You should be getting the uniform location for "myTexture"; that's the sampler's name, after all.
So I created a texture using glGenTexture() and attached it to a frame buffer
So in other words you did this:
glActiveTexture(GL_TEXTURE0);
GLuint myTexture;
glGenTextures(1, &myTexture);
glBindTexture(GL_TEXTURE_2D, myTexture);
// very important and often missed by newbies:
// A framebuffer attachment must be initialized but not bound
// when using the framebuffer, so it must be unbound after
// initializing
glTexImage2D(…, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(…);
glFramebufferTexture2D(…, myTexture, …);
Okay, so myTexture is the name handle for the texture. What do we have to do? Unbinding the FBO, so that we can use the attached texture as a image source, so:
glBindFrameuffer(…, 0);
glActiveTexture(GL_TEXTURE0 + n);
glBindTexture(GL_TEXTURE_2D, myTexture);
glUseProgram(…);
glUniformi(texture_sampler_location, n);

Issue with glGetTexLevelParameter

I'm having an issue trying to get the width and height of a texture using the glGetTexLevelParameter function. No matter what I try, the function will not set the value of the width or height variable. I checked for errors but keep getting no error. Here is my code (based off of the NeHe tutorials if that helps):
int LoadGLTextures()
{
//load image file directly into opengl as new texture
GLint width = 0;
GLint height = 0;
texture[0] = SOIL_load_OGL_texture("NeHe.bmp", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); //image must be in same place as lib
if(texture[0] == 0)
{
return false;
}
glEnable(GL_TEXTURE_2D);
glGenTextures(3, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //no filtering bc of GL_NEAREST, looks really bad
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
const GLubyte* test = gluErrorString(glGetError());
cout << test << endl;
return true;
}
I'm using visual studio 2010 also if that helps. The call to load texture[0] is from the SOIL image library.
Let's break this down:
This call loads an image, creates a new texture ID and loads the image into the texture object named by this ID. In case of success the ID is returned and stored in texture[0].
texture[0] = SOIL_load_OGL_texture(
"NeHe.bmp",
SOIL_LOAD_AUTO,
SOIL_CREATE_NEW_ID,
SOIL_FLAG_INVERT_Y);
BTW: The image file is not to be in the same directory as the library, but in the current working directory of the process at time of calling this function. If you didn't change the working directory, it's whatever directory your process got called from.
Check if the texture was loded successfully
if(texture[0] == 0)
{
return false;
}
Enabling texturing here makes only little sense. glEnable calls belong in the rendering code.
glEnable(GL_TEXTURE_2D);
Okay, here's a problem. glGenTextures generates new texture IDs and places them in the array provided to it. Whatever was stored in that array before is overwritten. In your case the very texture ID generated and returned by SOIL_load_OGL_texture. Note that this is just some handle and is not garbage collected in any way. You now have in face a texture object dangling in OpenGL and no longer access to it, because you threw away the handle.
glGenTextures(3, &texture[0]);
Now you bind a texture object named by the newly created ID. Since this is a new ID you're effectively creating a new texture object with no image data assigned.
glBindTexture(GL_TEXTURE_2D, texture[0]);
All the following calls operate on an entirely different texture than the one created by SOIL.
How to fix the code: Remove glGenTextures. In your case it's not only redundant, it's the cause of your problem.
This line:
texture[0] = SOIL_load_OGL_texture("NeHe.bmp", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
Creates a texture, storing the OpenGL texture in texture[0].
This line:
glGenTextures(3, &texture[0]);
Creates three textures, storing them in the texture array, overwriting whatever was there.
See the problem? You get a texture from SOIL, then you immediately throw it away by overwriting it with a newly-created texture.
This is no different conceptually than the following:
int *pInt = new int(5);
pInt = new int(10);
Hm, doesn't glGenTextures(howmany,where) work just like glGenBuffers? Why do you assign three textures to one pointer, how it's expected to work?
I think it shoud be
int textures[3];
glGenTextures(3,textures);
this way three generated texture buffers will be placed in texture array.
Or
int tex1, tex2, tex3;
glGenTextures(1,&tex1);
glGenTextures(1,&tex2);
glGenTextures(1,&tex3);
so you have three separate texture buffer pointers