understanding textures

OpenGL Texture Units and Samplers

When using multiple textures in a shader, 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);

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