My OpenGL program is really slow and for months I have not been able to figure out why. Before, I was using the fixed function pipeline and it ran incredibly smooth. I am trying to load .obj models in the programmable pipeline now but it is incredibly slow.
My Code:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/ext.hpp>
#include "glm/gtx/string_cast.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <fstream>
#include <vector>
//Global variables
float yaw = -90.0f, pitch,lastX,lastY;
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
//External functions
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
for(static bool firstMouse = true;firstMouse;firstMouse=false)
{
lastX = xpos;
lastY = ypos;
}
float xoffset = xpos - lastX, yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
xoffset,yoffset *= 0.255;//Sensitivity
yaw += xoffset;//Yaw is rotating the camera on the X axis
pitch += yoffset;//Pitch is rotating the camera
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));
cameraFront = glm::normalize(front);
}
void loadObj(const char * path, std::vector < glm::vec3 > & out_vertices, std::vector < glm::vec2 > & out_uvs, std::vector < glm::vec3 > & out_normals,const int facetype){
std::vector< unsigned int > vertexIndices, uvIndices, normalIndices;
std::vector< glm::vec2 > temp_uvs;
std::vector< glm::vec3 > temp_normals,temp_vertices;FILE * file = fopen(path, "r");
if( file == NULL ){
std::cout<<"ERROR::Could not load asset file, "<<path<<'
';
}
while( 1 ){
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
break; // EOF = End Of File. Quit the loop.
if ( strcmp( lineHeader, "v" ) == 0 ){
glm::vec3 vertex;
fscanf(file, "%f %f %f
", &vertex.x,&vertex.y,&vertex.z);
temp_vertices.push_back(vertex);
}else if ( strcmp( lineHeader, "vt" ) == 0 ){
glm::vec2 uv;
fscanf(file, "%f %f
", &uv.x, &uv.y );
temp_uvs.push_back(uv);
}else if ( strcmp( lineHeader, "vn" ) == 0 ){
glm::vec3 normal;
fscanf(file, "%f %f %f
", &normal.x, &normal.y, &normal.z );
temp_normals.push_back(normal);
}else if ( strcmp( lineHeader,"f" ) == 0 ){
std::string vertex1, vertex2, vertex3;
unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
if(facetype==0){
if (fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d
", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] ) != 9){
std::cout<<path<<" can't be read by our simple parser : ( Try exporting with other options
";
}
}else if(facetype==1){
if (fscanf(file, "%d %d %d
", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]) != 3){
std::cout<<path<<" can't be read by our simple parser : ( Try exporting with other options
";
}
}else if(facetype==2){
if (fscanf(file, "%d//%d %d//%d %d//%d
", &vertexIndex[0],&normalIndex[0], &vertexIndex[1],&normalIndex[1], &vertexIndex[2],&normalIndex[2]) != 6){
std::cout<<path<<" can't be read by our simple parser : ( Try exporting with other options
";
}
}
vertexIndices.push_back(vertexIndex[0]);
vertexIndices.push_back(vertexIndex[1]);
vertexIndices.push_back(vertexIndex[2]);
uvIndices .push_back(uvIndex[0]);
uvIndices .push_back(uvIndex[1]);
uvIndices .push_back(uvIndex[2]);
normalIndices.push_back(normalIndex[0]);
normalIndices.push_back(normalIndex[1]);
normalIndices.push_back(normalIndex[2]);
}
// For each vertex of each triangle
for( unsigned int i=0; i<vertexIndices.size(); ++i ){
unsigned int vertexIndex = vertexIndices[i];
glm::vec3 vertex = temp_vertices[ vertexIndex-1 ];
out_vertices.push_back(vertex);
}
}
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main() {
//Firstly
std::ios_base::sync_with_stdio(false);//Disables the synchronization between the C and C++ standard streams (though mixing C- and C++ style I/O will be a challenge)
std::cin.tie(NULL);//Unties cin from cout for faster cin and cout functions (this does mean that they are off sync but if you are not using cin than it doesnt matter)
//Local variables
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::mat4 view,project,model;
float lastFrame,frames=-1,current_time,last_time,cameraSpeed;
if(!glfwInit()) {
//Initialization failed
fprintf(stderr,"Failed to initialize GLFW
");//Initialization error
return -1;//A -1 returned to the main function indicates a unsuccessful run
}
//Window hints change the way the window works (like settings)
//glfwWindowHint(GLFW_SAMPLES,4);//4x antialiasing
//glEnable(GL_MULTISAMPLE);
//"Major" and "minor" are two components of a single version number, separated by a dot.
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);//Version 3...
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);//.3
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE);//To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);//We don't want the old OpenGL
const GLFWvidmode*mode=glfwGetVideoMode(glfwGetPrimaryMonitor());//Constant (not-changable) GLFWvideomode (variable type) is named "mode", is set to the primary monitor
glfwWindowHint(GLFW_RED_BITS,mode->redBits);
glfwWindowHint(GLFW_GREEN_BITS,mode->greenBits);
glfwWindowHint(GLFW_BLUE_BITS,mode->blueBits);
glfwWindowHint(GLFW_REFRESH_RATE,mode->refreshRate);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
float win_width=mode->width,win_height=mode->height;
GLFWwindow*window=glfwCreateWindow(win_width,win_height,"My Title",glfwGetPrimaryMonitor(),NULL);//Create window that is the size of "mode" (constant primary monitor), named "", on the primary monitor
if(window==NULL) { //If glfw window does not exist
fprintf(stderr,"Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible.
");
glfwTerminate();//End glfw processes
return -1;
}
glfwMakeContextCurrent(window);//Initialize GLEW
glewExperimental=true;//Needed in core profile
if(glewInit()!=GLEW_OK) { //If glew is not okay
fprintf(stderr,"Failed to initialize GLEW
");
return -1;
}
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);//Enable face culling (culling means to not render)
glCullFace(GL_BACK);//Prevent rendering triangles that are behind a shape (those that the player cannot see in the first place)
glfwSetCursorPosCallback(window,mouse_callback);//When mouse enters window, run mouse_callback function. It also gives mouse position coordinates to that function
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
const char*vertexSource=R"glsl(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
)glsl";
GLuint vertexShader=glCreateShader(GL_VERTEX_SHADER);//Creates shader object
glShaderSource(vertexShader,1,&vertexSource,NULL);//Loads data to shader object
glCompileShader(vertexShader);//Compiles shader object
//Vertex compile error message
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED
" << infoLog << '
';
}
const char* fragmentSource=R"glsl(
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main()
{
// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
float specularStrength = 0.16;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
)glsl";
//The fragment shader is compiled in exactly the same way
GLuint fragmentShader=glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader,1,&fragmentSource,NULL);
glCompileShader(fragmentShader);
//Fragment compile error message
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED
" << infoLog << '
';
}
//Combines shaders into one program
GLuint shaderProgram=glCreateProgram();//Create shader program
//Attatch vertex shader and fragment shader to new shader program
glAttachShader(shaderProgram,vertexShader);
glAttachShader(shaderProgram,fragmentShader);
glLinkProgram(shaderProgram);//Links shader program
//Shader program linking error message
int InfoLogLength;
glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength > 0) {
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(shaderProgram, InfoLogLength, NULL, &ProgramErrorMessage[0]);
std::cout<<&ProgramErrorMessage[0]<<'
';
}
//To actually start using the shaders in the program, you just have to call:
glUseProgram(shaderProgram);
// first, configure the cube's VAO (and VBO)
unsigned int VBO, cubeVAO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
std::vector< glm::vec3 > vertices,normals;
std::vector< glm::vec2 > uvs;
loadObj("teapot.obj",vertices,uvs,normals,1);
int vert_size=vertices.size();
glBufferData(GL_ARRAY_BUFFER, vert_size * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);//Now that our VBO is active we can copy the vertex data to it
glBindVertexArray(cubeVAO);
//position attribute
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);//Specify how the data for that input is retrieved from the array
glEnableVertexAttribArray(0);
//normal attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,0,0);
glEnableVertexAttribArray(1);
// second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)
/*unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// note that we update the lamp's position attribute's stride to reflect the updated buffer data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);*/
glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"projection"),1,GL_FALSE,glm::value_ptr(glm::perspective(45.0f,win_width/win_height,0.1f,100.0f)));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(glm::translate(glm::scale(glm::mat4(1.0f),glm::vec3(1.f)),glm::vec3(0.f,0.f,0.f))));
glProgramUniform3f(shaderProgram, glGetUniformLocation(shaderProgram, "objectColor"), 1.0f, 0.5f, 0.f);
glProgramUniform3f(shaderProgram, glGetUniformLocation(shaderProgram, "lightColor"), 1.0f, 1.0f, 1.0f);
glm::vec3 lightPos=glm::vec3(1.2f, 6.f, 8.f);
glProgramUniform3f(shaderProgram, glGetUniformLocation(shaderProgram, "lightPos"),lightPos.x,lightPos.y,lightPos.z);
glProgramUniform3f(shaderProgram, glGetUniformLocation(shaderProgram, "viewPos"),cameraPos.x,cameraPos.y,cameraPos.z);
//While the window is open
while(!glfwWindowShouldClose(window)){
//FPS counter (Frames per second)
current_time=glfwGetTime();
if (current_time - last_time < 1.0f){
++frames;
}else{
std::cout<<frames<<'
';
frames=0;
last_time=current_time;
}
cameraSpeed = 2.5f * (current_time - lastFrame);
lastFrame = current_time;
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS){
glfwSetWindowMonitor(window,NULL,0,0,win_width,win_height,GLFW_DONT_CARE);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
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, glm::vec3(0.0f, 1.0f, 0.0f))) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += glm::normalize(glm::cross(cameraFront, glm::vec3(0.0f, 1.0f, 0.0f))) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
cameraPos += cameraSpeed * glm::vec3(0.0f, 1.0f, 0.0f);
if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
cameraPos -= cameraSpeed * glm::vec3(0.0f, 1.0f, 0.0f);
/*if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS){
lightPos.z-=0.5;
std::cout<<glm::to_string(lightPos)<<'
';
}
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS){
lightPos.z+=0.5;
std::cout<<glm::to_string(lightPos)<<'
';
}
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS){
lightPos.x+=0.5;
std::cout<<glm::to_string(lightPos)<<'
';
}
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS){
lightPos.x-=0.5;
std::cout<<glm::to_string(lightPos)<<'
';
}
if (glfwGetKey(window, GLFW_KEY_EQUAL) == GLFW_PRESS){
lightPos.y+=0.5;
std::cout<<glm::to_string(lightPos)<<'
';
}
if (glfwGetKey(window, GLFW_KEY_MINUS) == GLFW_PRESS){
lightPos.y-=0.5;
std::cout<<glm::to_string(lightPos)<<'
';
}*/
glClearColor(0.5f,0.5f,0.5f,0.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(glm::lookAt(cameraPos, cameraPos + cameraFront, glm::vec3(0.0f, 1.0f, 0.0f))));
glProgramUniform3f(shaderProgram, glGetUniformLocation(shaderProgram, "viewPos"),cameraPos.x,cameraPos.y,cameraPos.z);
//glProgramUniform3f(shaderProgram, glGetUniformLocation(shaderProgram, "lightPos"),lightPos.x,lightPos.y,lightPos.z);
glDrawArrays(GL_TRIANGLES,0,vert_size);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1,&cubeVAO);
glDeleteBuffers(1,&VBO);
glfwTerminate();
return 0;//A 0 returned to the main function indicates a successful run
}