OpenGL 4.0 texture binding

opengl

I'm trying to bind multiple textures to the samplers in my fragment shader. The loading code seems to work well. ATI's CodeXL shows the texture being loaded correctly.

However, when I go to bind the textures for my model to Active textures 0 and 1 I can not get it to send the value to my shader. When I have the shader uniform marked as a usampler2D and use uvec4 to store the color, like I should since my texture is provided as unsigned bytes, I get an all white model. When I change the shader uniform to be a sampler2D and use a vec4 to store the color, my glUniform1i call can no longer get the location of the shader variable and so nothing gets set for the active texture. This results in the diffuse texture being able to be used, but I can not get the normal texture. On the bright side, The diffuse texture is being drawn on the model this way.

I'm not sure what the problem is. I've check several places online trying to figure it out, and I have looked through the redbook. I know I'm missing something, or have some state set wrong, but I can't seem to find it. Thank you in advance for any help you can give me to fix this problem.

Texture Creation

int[] testWidth;

testWidth = new int[1];
testWidth[0] = 1000;

// First bind the texture.
bind();

// Make sure that textures are enabled.
// I read that ATI cards need this before MipMapping.
glEnable(GL_TEXTURE_2D);

// Test to make sure we can create a texture like this.
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, format, width, height,
             0, format, GL_UNSIGNED_BYTE, null);
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
                         testWidth);
if (testWidth[0] == 0)
{
   message("Could not load texture onto the graphics card.");
}
else
{
   // Not so sure about this part....but it seems to work.
   glPixelStorei(GL_PACK_ALIGNMENT, 1);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   // Load the texture data.
   glTexImage2D(texture_type, 0, format, width, height,
                0, format, GL_UNSIGNED_BYTE, (GLvoid[]?)value);

   // Smaller mipmaps need linear mipmap coords.
   // Larger just uses linear of the main texture.
   glTexParameterf(texture_type, GL_TEXTURE_MIN_FILTER,
                   GL_LINEAR_MIPMAP_LINEAR);
   glTexParameterf(texture_type, GL_TEXTURE_MAG_FILTER,
                   GL_LINEAR);

   // Clamp the texture to the edges.
   glTexParameterf(texture_type, GL_TEXTURE_WRAP_S,
                   GL_CLAMP_TO_EDGE);
   glTexParameterf(texture_type, GL_TEXTURE_WRAP_T,
                   GL_CLAMP_TO_EDGE);
   glTexParameterf(texture_type, GL_TEXTURE_WRAP_R,
                   GL_CLAMP_TO_EDGE);

   // Generate the mipmaps. The tex parameter is there
   // for ATI cards. Again, it's something I read online.
   glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
   glGenerateMipmap(texture_type);
}

// Now unbind the texture.
unbind();

Texture Binding

if (currentShader != null)
{
   currentShader.set_uniform_matrix("model_matrix", ref model_matrix,
                                    true);

   if (material != null)
   {
      if (material.diffuse_texture != null)
      {
         glActiveTexture(GL_TEXTURE0);
         material.diffuse_texture.bind();
         currentShader.set_uniform_texture("diffuse_texture",
                                           Constants.DIFFUSE_TEXTURE);
      if (material.normal_testure != null)
      {
         glActiveTexture(GL_TEXTURE1);
         material.normal_texture.bind();
         currentShader.set_uniform_texture("normal_texture",
                                           Constants.NORMAL_TEXTURE);
      }
   }
}

// If there is a renderable then render it.
if (renderable != null)
{
   renderable.render(1.0);
}

if (material != null)
{
   material.unbind();
}

Fragment Shader

#version 400 core

/**
 * Smooth the inward vertex color. Smooth it so that the fragments
 * which will be in between the vertices as well can get a value close
 * to where they are positioned after being rasterized.
 */
smooth in vec4 vertex_color;

/**
 * Smooth the inward texture coordinates. Smooth it so that the
 * fragments which will be in between the vertices as well can get a
 * value close to where they are positioned after being rasterized.
 */
smooth in vec2 out_texture_coordinate;

/**
 * The color to make this fragment.
 */
out vec4 frag_color;

/**
 * The models diffuse texture. This will be mapped to index 0.
 */
uniform usampler2D diffuse_texture;

/**
 * The models normal texture. This will be mapped to index 1.
 */
uniform usampler2D normal_texture;


/**
 * The starting function of the shader.
 */
void main(void)
{
   uvec4 diffuseColor;
   uvec4 normalModifier;

   diffuseColor = texture(diffuse_texture, out_texture_coordinate);
   normalModifier = texture(normal_texture, out_texture_coordinate);

   // Discard any fragments that have an alpha color less than 0.05.
   if (diffuseColor.a < 1.0)
   {
      // This works as part of depth testing to remove the fragments that
      // are not useful.
      discard;
   }

   frag_color = diffuseColor;
}

Uniform Setting

/**
   * Sets the uniform value for a texture in the shader.
   *
   * @param name The name of the uniform to bind this texture to.
   * This must have already been registered.
   *
   * @param textureUnit The id for the texture unit to bind to the uniform.
   * This is not the texture's id/reference, but the OpenGL texture unit
   * that the reference is bound to.
   * This is set by calling glActiveTexture.
   */
  public void set_uniform_texture(string name, int textureUnit)
  {
     // Check to make sure the uniform was given a location already.
     if (register_uniform(name) == true)
     {
        // Set the data for this uniform then.
        glUniform1i(uniform_mapping.get(name), textureUnit);
     }
     else
     {
        message("Texture was not set. %s", name);
     }
  }

  /**
   * Register a uniform for passing data to the shader program.
   *
   * @return true if the uniform was found with a valid location;
   * otherwise, false.
   *
   * @param name The name for the parameter to get a uniform location for.
   * Use this name for the variable in your shader.
   */
  public bool register_uniform(string name)
  {
     int location;

     // Make sure we didn't already get the location of the uniform value.
     if (uniform_mapping.has_key(name) == false)
     {
        location = Constants.OPENGL_INVALID_INDEX;

        // We have no information about this uniform, so try
        // to get it's location.
        location = glGetUniformLocation(reference, name);

        // The location will 0 or higher if we found the uniform.
        if (location != Constants.OPENGL_INVALID_INDEX)
        {
           uniform_mapping.set(name, location);

           return true;
        }
     }
     else
     {
        // The uniform was previously found and can be used.
        return true;
     }

     debug("Uniform %s not found!!!!!", name);
     return false;
  }

Best Answer

Setting the internal format to GL_RGB/A implies you should be using a sampler2D and not usampler2D, even though the raw image data is initially given as unsigned bytes. EDIT The given data gets converted to the internal format at the call to glTexImage2D (in this case GL_RGBA is 8 bits per channel so not much has to happen). However, for most graphics applications the data is needed with higher accuracy, for example when sampling the texture with non-"nearest" interpolation, which is why it's normally exposed as floats.

To bind multiple textures...

glActiveTexture(GL_TEXTURE0 + firstTextureIndex); //firstTextureIndex should be unique amongst the textures bound for a particular shader
glBindTexture(GL_TEXTURE_2D, myFirstTextureHandle);
glUniform1i(glGetUniformLocation(shaderProgramHandle, "firstSampler"), firstTextureIndex); //note the same index given in the glActiveTexture call. This is also always glUniform1i

and repeat for secondTextureIndex, mySecondTextureHandle, "secondSampler" etc.

If glGetUniformLocation doesn't return a location, double check you actually use it and it affects the shader output (or it gets optimized out completely). Also check for the usual typos or missing "uniform" keyword etc.

Related Topic