understanding textures

OpenGL Texture Units and Samplers

Before getting started here I recommend reading this article as well if you're still confused.

As mentioned the texture unit is the processor; the texture object holds the data that is processed and for each image you want to use in your shader you need a texture unit, and thus you need to bind each texture to a different texture unit. Each texture unit acts as a slot where you can attach a texture, and you will need to ensure that your shader can sample from each texture unit accordingly.

Example: Drawing a Crate with Diffuse and Specular Maps

Let's expand on the example of drawing a crate with both a diffuse map (base color) and a specular map (shininess). Here’s how you would handle this in OpenGL:

1. Load and Bind Textures

You need to load your textures (diffuse and specular) and bind them to different texture units. For this example, we'll use texture units GL_TEXTURE0 for the diffuse map and GL_TEXTURE1 for the specular map.

Load Textures

#include <stb_image.h>

// Load diffuse texture
GLuint diffuseTexture;
glGenTextures(1, &diffuseTexture);
glBindTexture(GL_TEXTURE_2D, diffuseTexture);
int width, height, nrChannels;
unsigned char *data = stbi_load("diffuse_map.jpg", &width, &height, &nrChannels, 0);
if (data)
{
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
    std::cout << "Failed to load diffuse texture" << std::endl;
}
stbi_image_free(data);

// Load specular texture
GLuint specularTexture;
glGenTextures(1, &specularTexture);
glBindTexture(GL_TEXTURE_2D, specularTexture);
data = stbi_load("specular_map.jpg", &width, &height, &nrChannels, 0);
if (data)
{
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
    std::cout << "Failed to load specular texture" << std::endl;
}
stbi_image_free(data);

Bind Textures to Texture Units

// Bind diffuse texture to texture unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseTexture);

// Bind specular texture to texture unit 1
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularTexture);
note that if you don't do the above it'll probably be the case that one of the images will load up, this is because the default value is usually 0 in the sampler and so it grabs whatever is in that texture unit, and so things might just happen to work out out of chance

2. Set Up Shader Program

Compile and link your shader program:

Vertex Shader (vertex_shader.glsl)

#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

Fragment Shader (fragment_shader.glsl)

#version 330 core
in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D diffuseTexture;
uniform sampler2D specularTexture;

void main()
{
    vec3 diffuseColor = texture(diffuseTexture, TexCoord).rgb;
    float specularFactor = texture(specularTexture, TexCoord).r;
    vec3 finalColor = diffuseColor * (0.2 + 0.8 * specularFactor); // Example calculation
    FragColor = vec4(finalColor, 1.0);
}

3. Set Shader Uniforms

Before rendering, you need to tell the shader which texture unit corresponds to each sampler.

GLuint shaderProgram = // Compile and link shaders here;
glUseProgram(shaderProgram);

// Set the diffuse texture sampler to use texture unit 0
glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0);

// Set the specular texture sampler to use texture unit 1
glUniform1i(glGetUniformLocation(shaderProgram, "specularTexture"), 1);

4. Rendering Loop

In the rendering loop, you use the shader program and draw your objects.

while (!glfwWindowShouldClose(window))
{
    // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Bind shader program
    glUseProgram(shaderProgram);

    // Bind the textures
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, diffuseTexture);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, specularTexture);

    // Draw your object
    glBindVertexArray(VAO); // Assuming VAO is set up
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
    
    // Swap buffers
    glfwSwapBuffers(window);
    glfwPollEvents();
}

Summary


edit this page