Hello! I’m going through a tutorial on the basics of OpenGL and I cannot figure out why my cube isn’t being shown on screen. When I run the program nothing about my cube appears. I think the problem is happening somewhere in my vertex shader where I multiply the projection, view, and model matrices with my vertex positions. If I remove the multiplication between the three matrices in my vertex shader, I can see my 2d texture, so I think something is wrong with the matrix calculations. I’ve tried everything I can think of (given my 3 days of experience) and have searched the internet for a few hours with no luck. Any help is greatly appreciated!
Here’s my code:
Main.cpp
#include <iostream>
#include <sstream>
#define GLEW_STATIC
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "glm/gtc/matrix_transform.hpp"
#include "ShaderProgram.h"
#include "Texture2D.h"
const char* WINDOW_TITLE = "OpenGL";
int WINDOW_WIDTH = 1024;
int WINDOW_HEIGHT = 768;
GLFWwindow* gWindow = NULL;
bool gWireframe = false;
const std::string texture1FileName = "crate.png";
void glfw_onKey(GLFWwindow* window, int key, int scanCode, int action, int mode);
void glfw_OnFrameBufferSize(GLFWwindow* window, int width, int height);
void showFPS(GLFWwindow* window);
bool initOpenGL();
int main()
{
if (!initOpenGL())
{
std::cerr << "Initialization failed!" << std::endl;
return - 1;
}
GLfloat vertices[] = {
// Position // Tex coords
// Front face
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
// Back face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
// Left face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
// Right face
1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
// Top face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
// Bottom face
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
};
glm::vec3 cubePos = glm::vec3(0.0f, 0.0f, -5.0f);
GLuint vbo; // Vertex Buffer Object
GLuint vao; // Vertex Array Object
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), NULL);
glEnableVertexAttribArray(0);
// Tex Coords
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
ShaderProgram shaderProgram;
shaderProgram.loadShader("basic.vert", "basic.frag");
Texture2D texture1;
texture1.loadTexture(texture1FileName, true);
float cubeAngle = 0.0f;
double lastTime = glfwGetTime();
// Main loop
while (!glfwWindowShouldClose(gWindow))
{
showFPS(gWindow);
double currentTime = glfwGetTime();
double deltaTime = currentTime - lastTime;
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
texture1.bind(0);
cubeAngle += (float)(deltaTime * 50.0f);
if (cubeAngle >= 360.0)
{
cubeAngle = 0.0f;
}
glm::vec3 camPos(0.0f, 0.0f, 0.0f);
glm::vec3 targetPos(0.0f, 0.0f, -1.0f);
glm::vec3 up(0.0f, 1.0f, 0.0f);
glm::mat4 model = glm::translate(model, cubePos) * glm::rotate(model, glm::radians(cubeAngle), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 view = glm::lookAt(camPos, targetPos, up);
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f);
shaderProgram.use();
shaderProgram.setUniform("model", model);
shaderProgram.setUniform("view", view);
shaderProgram.setUniform("projection", projection);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glfwSwapBuffers(gWindow);
lastTime = currentTime;
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}
bool initOpenGL()
{
if (!glfwInit())
{
std::cerr << "GLFW initialization failed" << std::endl;
return false;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
gWindow = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL);
if (gWindow == NULL)
{
std::cerr << "Failed to create GLFW window!" << std::endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(gWindow);
glfwSetKeyCallback(gWindow, glfw_onKey);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cerr << "GLEW initialization failed" << std::endl;
glfwTerminate();
return false;
}
glClearColor(0.23f, 0.30f, 0.47f, 1.0f);
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
return true;
}
void glfw_onKey(GLFWwindow* window, int key, int scanCode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
if (key == GLFW_KEY_W && action == GLFW_PRESS)
{
gWireframe = !gWireframe;
if (gWireframe)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
}
void glfw_OnFrameBufferSize(GLFWwindow* window, int width, int height)
{
WINDOW_WIDTH = width;
WINDOW_HEIGHT = height;
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}
void showFPS(GLFWwindow* window)
{
static double previousSeconds = 0.0;
static int frameCount = 0;
double elapsedSeconds;
double currentSeconds = glfwGetTime(); // returns number of seconds since GLFW started
elapsedSeconds = currentSeconds - previousSeconds;
// Limit text update 4 times per second
if (elapsedSeconds > 0.25)
{
previousSeconds = currentSeconds;
double fps = (double)frameCount / elapsedSeconds;
double msPerFrame = 1000.0 / fps;
std::ostringstream outs;
outs.precision(3);
outs << std::fixed
<< WINDOW_TITLE << " - "
<< "FPS: " << fps << " "
<< "Frame Time: " << msPerFrame << " (ms)";
glfwSetWindowTitle(window, outs.str().c_str());
frameCount = 0;
}
frameCount++;
}
basic.vert
#version 330 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec2 TexCoord;
void main()
{
gl_Position = projection * view * model * vec4(pos, 1.0f);
TexCoord = texCoord;
}
basic.frag
#version 330 core
in vec2 TexCoord;
out vec4 frag_color;
uniform sampler2D myTexture1;
//uniform sampler2D myTexture2;
void main()
{
frag_color = texture(myTexture1, TexCoord);
//frag_color = vec4(1.0f, 1.0f, 0.0f, 1.0f);
}
ShaderProgram.cpp
#include "ShaderProgram.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include "glm/gtc/type_ptr.hpp"
ShaderProgram::ShaderProgram()
{
mHandle = 0;
}
ShaderProgram::~ShaderProgram()
{
glDeleteProgram(mHandle);
}
bool ShaderProgram::loadShader(const char* vsFilename, const char* fsFilename)
{
string vsString = fileToString(vsFilename);
string fsString = fileToString(fsFilename);
const GLchar* vsSourcePtr = vsString.c_str();
const GLchar* fsSourcePtr = fsString.c_str();
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vs, 1, &vsSourcePtr, NULL);
glShaderSource(fs, 1, &fsSourcePtr, NULL);
glCompileShader(vs);
checkCompileErrors(vs, ShaderType::VERTEX);
glCompileShader(fs);
checkCompileErrors(fs, ShaderType::FRAGMENT);
mHandle = glCreateProgram();
glAttachShader(mHandle, vs);
glAttachShader(mHandle, fs);
glLinkProgram(mHandle);
checkCompileErrors(mHandle, ShaderType::PROGRAM);
glDeleteShader(vs);
glDeleteShader(fs);
mUniformLocation.clear();
return true;
}
void ShaderProgram::use()
{
if (mHandle)
{
glUseProgram(mHandle);
}
}
string ShaderProgram::fileToString(const string& filename)
{
std::stringstream ss;
std::ifstream file;
try
{
file.open(filename, std::ios::in);
if (!file.fail())
{
ss << file.rdbuf();
}
file.close();
}
catch (const std::exception ex)
{
std::cerr << "Error reading shader file!" << std::endl;
}
return ss.str();
}
void ShaderProgram::checkCompileErrors(GLuint shader, ShaderType type)
{
int status = 0;
if (type == ShaderType::PROGRAM)
{
glGetProgramiv(mHandle, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint length = 0;
glGetProgramiv(mHandle, GL_INFO_LOG_LENGTH, &length);
string errorLog(length, ' ');
glGetProgramInfoLog(mHandle, length, &length, &errorLog[0]);
std::cerr << "Error! Program failed to link. " << errorLog << std::endl;
}
}
else // VERTEX or FRAGMENT
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint length = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
string errorLog(length, ' ');
glGetShaderInfoLog(shader, length, &length, &errorLog[0]);
std::cerr << "Error! Shader failed to compile. " << errorLog << std::endl;
}
}
}
GLuint ShaderProgram::getProgram()const
{
return mHandle;
}
GLint ShaderProgram::getUniformLocation(const GLchar* name)
{
std::map<string, GLint>::iterator it = mUniformLocation.find(name);
if (it == mUniformLocation.end())
{
mUniformLocation[name] = glGetUniformLocation(mHandle, name);
}
return mUniformLocation[name];
}
void ShaderProgram::setUniform(const GLchar* name, const glm::vec2& v)
{
GLint loc = getUniformLocation(name);
glUniform2f(loc, v.x, v.y);
}
void ShaderProgram::setUniform(const GLchar* name, const glm::vec3& v)
{
GLint loc = getUniformLocation(name);
glUniform3f(loc, v.x, v.y, v.z);
}
void ShaderProgram::setUniform(const GLchar* name, const glm::vec4& v)
{
GLint loc = getUniformLocation(name);
glUniform4f(loc, v.x, v.y, v.z, v.w);
}
void ShaderProgram::setUniform(const GLchar* name, const glm::mat4& m)
{
GLint loc = getUniformLocation(name);
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(m));
}
Texture2D.cpp
#include "Texture2D.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#include "iostream"
Texture2D::Texture2D()
{
mTexture = 0;
}
Texture2D::~Texture2D()
{
}
bool Texture2D::loadTexture(const string& filename, bool generateMipMaps)
{
int width, height, components;
unsigned char* imageData = stbi_load(filename.c_str(), &width, &height, &components, STBI_rgb_alpha);
if (imageData == NULL)
{
std::cerr << "Error! Failed to load texture. " << filename << std::endl;
return false;
}
// Invert image
int widthInBytes = width * 4;
unsigned char* top = NULL;
unsigned char* bottom = NULL;
unsigned char temp = 0;
int halfHeight = height / 2;
for (int row = 0; row < halfHeight; row++)
{
top = imageData + row * widthInBytes;
bottom = imageData + (height - row - 1) * widthInBytes;
for (int col = 0; col < widthInBytes; col++)
{
temp = *top;
*top = *bottom;
*bottom = temp;
top++;
bottom++;
}
}
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
if (generateMipMaps)
{
glGenerateMipmap(GL_TEXTURE_2D);
}
stbi_image_free(imageData);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void Texture2D::bind(GLuint texUnit)
{
glActiveTexture(GL_TEXTURE0 + texUnit);
glBindTexture(GL_TEXTURE_2D, mTexture);
}
If you made it this far, thank you so much!