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?
You forgot to set up the vertex attribute pointer for aTexCoord.