OpenGL: Texture Loading

64 views Asked by At

I'm creating a 3D pyramid and loading it with a brick wall texture.

I had the pyramid working originally, but when I attempted to add the texture it fails to load and I'm left with a black window:

#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

// Vertex shader source code
const char* vertexShaderSource = R"(
    #version 330 core
    layout(location = 0) in vec3 aPos;
    layout(location = 1) in vec3 aColor;
    layout(location = 2) in vec2 aTexCoord;

    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

    out vec3 fragColor;
    out vec2 TexCoord;

    void main()
    {
        gl_Position = projection * view * model * vec4(aPos, 1.0);
        fragColor = aColor;
        TexCoord = aTexCoord;
    }
)";

// Fragment shader source code
const char* fragmentShaderSource = R"(
    #version 330 core
    in vec3 fragColor;
    in vec2 TexCoord;
    out vec4 FragColor;

    uniform sampler2D ourTexture;

    void main()
    {
        FragColor = texture(ourTexture, TexCoord);
    }
)";

bool firstMouse = true;
float lastX = 400, lastY = 300;
float yaw = -90.0f, pitch = 0.0f;
float fov = 45.0f;

glm::vec3 cameraPos = glm::vec3(0.4f, 0.4f, 0.4f);
glm::vec3 cameraFront = glm::vec3(-0.1f, -0.1f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);

glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 10.0f);

//Key navigation function
void processInput(GLFWwindow* window) {
    const float cameraSpeed = 0.001f;

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        cameraPos += cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        cameraPos -= cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
        cameraPos += cameraSpeed * cameraUp;
    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
        cameraPos -= cameraSpeed * cameraUp;
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
}

//Mouse cursor navigation callback function
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
    if (firstMouse) {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates range from bottom to top
    lastX = xpos;
    lastY = ypos;

    const float sensitivity = 0.1f;
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;

    // Constrain pitch to avoid flipping
    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    view = glm::lookAt(cameraPos, cameraPos + front, cameraUp);
}

//Mouse scroll wheel callback function
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
    if (fov >= 1.0f && fov <= 45.0f)
        fov -= yoffset;
    if (fov <= 1.0f)
        fov = 1.0f;
    if (fov >= 45.0f)
        fov = 45.0f;

    projection = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 10.0f);
}

int main()
{
    // Initialize GLFW
    if (!glfwInit())
    {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    // Configure GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    // Create a GLFW window
    GLFWwindow* window = glfwCreateWindow(800, 600, "Nick's Pyramid", nullptr, nullptr);
    if (!window)
    {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // Make the OpenGL context current
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    if (glewInit() != GLEW_OK)
    {
        std::cerr << "Failed to initialize GLEW" << std::endl;
        return -1;
    }

    // Define pyramid vertex data (positions and colors)
    float vertices[] = {
        // Base
        -0.5f, -0.5f, -0.5f, 0.3f, 0.7f, 0.9f, 1.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 0.7f, 1.0f, 0.3f, 1.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 0.5f, 0.2f, 0.4f, 0.0f, 0.0f, 
        -0.5f, -0.5f,  0.5f, 0.5f, 0.2f, 0.4f, 0.0f, 1.0f,

        // Front
         0.0f,  0.5f,  0.0f, 0.3f, 0.7f, 0.9f, 0.0f, 0.0f,
        -0.5f, -0.5f,  0.5f, 0.7f, 1.0f, 0.3f, 1.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 0.5f, 0.2f, 0.4f, 0.5f, 1.0f,

         // Right
          0.0f,  0.5f,  0.0f, 0.3f, 0.7f, 0.9f, 0.0f, 0.0f,
          0.5f, -0.5f,  0.5f, 0.7f, 1.0f, 0.3f, 1.0f, 0.0f,
          0.5f, -0.5f, -0.5f, 0.5f, 0.2f, 0.4f, 0.5f, 1.0f,

          // Back
           0.0f,  0.5f,  0.0f, 0.3f, 0.7f, 0.9f, 0.0f, 0.0f,
           0.5f, -0.5f, -0.5f, 0.7f, 1.0f, 0.3f, 1.0f, 0.0f, 
          -0.5f, -0.5f, -0.5f, 0.5f, 0.2f, 0.4f, 0.5f, 1.0f,

          // Left
           0.0f,  0.5f,  0.0f, 0.3f, 0.7f, 0.9f, 0.0f, 0.0f, 
          -0.5f, -0.5f, -0.5f, 0.7f, 1.0f, 0.3f, 1.0f, 0.0f, 
          -0.5f, -0.5f,  0.5f, 0.5f, 0.2f, 0.4f, 0.5f, 1.0f
    };

    //Define texture coordinates
    float texCoords[] = {
        0.0f, 0.0f,  //Lower-left corner
        1.0f, 0.0f,  //Lower-right corner
        0.5f, 1.0f   //top-center corner
    };

    //Generate texture
    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    // set the texture wrapping/filtering options (on the currently bound texture object)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load and generate the texture
    int width, height, nrChannels;
    unsigned char* data = stbi_load("brick_texture.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 texture" << std::endl;
    }
    stbi_image_free(data);

    // Define pyramid indices
    unsigned int indices[] = {
        0, 1, 2, // Base
        0, 2, 3,
        4, 5, 6, // Front
        7, 8, 9, // Right
        10, 11, 12, // Back
        13, 14, 15 // Left
    };

    // Compile and link shaders
    unsigned int vertexShader, fragmentShader, shaderProgram;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // Create Vertex Array Object (VAO), Vertex Buffer Object (VBO), and Element Buffer Object (EBO)
    unsigned int VAO, VBO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    // Bind the VAO
    glBindVertexArray(VAO);

    // Bind and fill vertex buffer
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Bind and fill element buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Set vertex attribute pointers
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // Enable depth testing
    glEnable(GL_DEPTH_TEST);

    // Use the shader program
    glUseProgram(shaderProgram);

    // Set uniform matrices
    unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model");
    unsigned int viewLoc = glGetUniformLocation(shaderProgram, "view");
    unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));


    // Set mouse navigation callbacks
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // Main rendering loop
    while (!glfwWindowShouldClose(window))
    {
        // Clear the screen and depth buffer
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Define model matrix (rotation)
        glm::mat4 model = glm::mat4(1.0f);
        float angle = (float)glfwGetTime();
        //model = glm::rotate(model, angle, glm::vec3(0.0f, 1.0f, 0.0f));
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

        // Draw the pyramid
        glActiveTexture(GL_TEXTURE0);  // Activate texture unit 0
        glBindTexture(GL_TEXTURE_2D, texture);
        glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0);  // Set the texture unit
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, 0);

        processInput(window);

        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Swap buffers and poll events
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // Clean up
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(shaderProgram);

    glfwTerminate();
    return 0;
}

The texture exists in the project folder under /resources/textures/, I've tried including the whole path, but still nothing. The console doesn't seem to produce any helpful messages either...

What am I doing wrong?

1

There are 1 answers

0
user253751 On

You forgot to set up the vertex attribute pointer for aTexCoord.