int main(int argc, char* argv[]) {
  if(argc < 5) {
    std::cout << "Usage: " << argv[0] << " originalMeshOBJ flattenedMeshOBJ textureImage flattenedImage" << std::endl;
    return EXIT_FAILURE;
  }
  char* originalMeshName = argv[1];
  char* flattenedMeshName = argv[2];
  char* textureImageName = argv[3];
  char* flattenedImageName = argv[4];

  cv::Mat textureImage = cv::imread(textureImageName);
  int width, height;
  width = textureImage.cols;
  height = textureImage.rows;
  textureImage.release();
  // std::cout << "Width: " << width << std::endl
  // 	    << "Height: " << height << std::endl;
  
  GLFWwindow* window;
  GLinit(&window, width, height);

  // int maxSize;
  // const GLubyte* version;
  // glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
  // std::cout << "Max texture size: " << maxSize << std::endl;
  // version = glGetString(GL_VERSION);
  // std::cout << "Version: " << version << std::endl;

  // Create Vertex Array Object
  GLuint vao;
  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);

  // Create a Vertex Buffer Object and copy the vertex data to it
  GLuint vbo;
  glGenBuffers(1, &vbo);

  GLfloat vertices[] = {
    // coord, color, texcoord
    -1.0f,  1.0f, 0.0f, 0.0f, // Top-left
    1.0f,  1.0f, 1.0f, 0.0f, // Top-right
    1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
    -1.0f, -1.0f, 0.0f, 1.0f  // Bottom-left
  };

  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

  GLuint ebo;
  glGenBuffers(1, &ebo);

  GLuint elements[] = {
    0, 1, 2,
    2, 3, 0
  };

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

  GLuint frameBuffer;
  glGenFramebuffers(1, &frameBuffer);

  glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

  GLuint texColorBuffer;
  glGenTextures(1, &texColorBuffer);
  glBindTexture(GL_TEXTURE_2D, texColorBuffer);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);

  GLuint vertexShader, fragmentShader, shaderProgram;
  createShaderProgram(vertexSource, fragmentSource, vertexShader, fragmentShader, shaderProgram);

  specifyScreenVertexAttributes(shaderProgram);

  cv::Mat img;
  img.create(height, width, CV_8UC3);

    GLuint red, blue, green;
    loadTextures(textureImageName, red, blue, green);

    // Set texture background color
    // float color[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    // glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);

    // Clear the screen to black
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // Draw
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);  

    glTexToCVmat(img, width, height);

    // glDeleteTextures(1, &tex);

  cv::imwrite(flattenedImageName, img);

  // Swap buffers
  glfwSwapBuffers(window);

  glDeleteProgram(shaderProgram);
  glDeleteShader(fragmentShader);
  glDeleteShader(vertexShader);

  glDeleteBuffers(1, &ebo);
  glDeleteBuffers(1, &vbo);
  glDeleteVertexArrays(1, &vao);

  glDeleteFramebuffers(1, &frameBuffer);

  glfwTerminate();

  return 0;
}
Пример #2
0
int main()
{
	GLFWwindow* window;
	const int WIDTH = 800, HEIGHT = 600;
	char* WINDOW_NAME = "OpenGL - basic stuff";
	initContext();
	window = glfwCreateWindow(WIDTH, HEIGHT, WINDOW_NAME, nullptr, nullptr);
	glfwMakeContextCurrent(window);

	glfwSetKeyCallback(window, key_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);
	//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	initGLEW();

	// Create VAOs
	GLuint vaoCube, vaoQuad, vaoLight;
	glGenVertexArrays(1, &vaoCube);
	glGenVertexArrays(1, &vaoQuad);
	glGenVertexArrays(1, &vaoLight);

	// Load vertex data
	GLuint vboCube, vboQuad;
	glGenBuffers(1, &vboCube);
	glGenBuffers(1, &vboQuad);

	glBindBuffer(GL_ARRAY_BUFFER, vboCube);
	glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vboQuad);
	glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);

	// Create shader programs
	GLuint sceneVertexShader, sceneFragmentShader, sceneShaderProgram;
	createShaderProgram(readShaderFile("basic.vert"), readShaderFile("basic.frag"), sceneVertexShader, sceneFragmentShader, sceneShaderProgram);

	GLuint screenVertexShader, screenFragmentShader, screenShaderProgram;
	createShaderProgram(readShaderFile("basic_screen.vert"), readShaderFile("basic_screen.frag"), screenVertexShader, screenFragmentShader, screenShaderProgram);

	// Specify the layout of the vertex data
	glBindVertexArray(vaoCube);
	glBindBuffer(GL_ARRAY_BUFFER, vboCube);
	specifySceneVertexAttributes(sceneShaderProgram);

	glBindVertexArray(vaoQuad);
	glBindBuffer(GL_ARRAY_BUFFER, vboQuad);
	specifyScreenVertexAttributes(screenShaderProgram);

	glBindVertexArray(vaoLight);
	glBindBuffer(GL_ARRAY_BUFFER, vboCube);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0); // Note that we skip over the other data in our buffer object (we don't need the normals/textures, only positions).
	glEnableVertexAttribArray(0);
	glBindVertexArray(0);

	// Load textures
	GLuint diffuseMap = loadTextureMap("container2.png");
	GLuint specularMap = loadTextureMap("container2_specular.png");

	glUseProgram(sceneShaderProgram);
	glUniform1i(glGetUniformLocation(sceneShaderProgram, "texKitten"), 0);
	glUniform1i(glGetUniformLocation(sceneShaderProgram, "texPuppy"), 1);

	glUseProgram(screenShaderProgram);
	glUniform1i(glGetUniformLocation(screenShaderProgram, "texFramebuffer"), 0);

	GLint uniModel = glGetUniformLocation(sceneShaderProgram, "model");

	// Create frame buffer
	GLuint frameBuffer;
	glGenFramebuffers(1, &frameBuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

	// Create texture to hold color buffer
	GLuint texColorBuffer;
	glGenTextures(1, &texColorBuffer);
	glBindTexture(GL_TEXTURE_2D, texColorBuffer);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);

	// Create Renderbuffer Object to hold depth and stencil buffers
	GLuint rboDepthStencil;
	glGenRenderbuffers(1, &rboDepthStencil);
	glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboDepthStencil);	

	glm::mat4 viewMatrix = glm::lookAt(
		glm::vec3(2.5f, 2.5f, 2.0f),
		glm::vec3(0.0f, 0.0f, 0.0f),
		glm::vec3(0.0f, 0.0f, 1.0f));
	GLint uniView = glGetUniformLocation(sceneShaderProgram, "view");
	glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(viewMatrix));

	glm::mat4 projMatrix = glm::perspective(camera.Zoom, (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f);
	GLint uniProj = glGetUniformLocation(sceneShaderProgram, "proj");
	glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(projMatrix));

	GLint uniColor = glGetUniformLocation(sceneShaderProgram, "overrideColor");

	GLint timer = glGetUniformLocation(sceneShaderProgram, "timer");

	while (!glfwWindowShouldClose(window))
	{
		GLfloat currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;

		glfwPollEvents();
		Do_Movement();

		// Bind our framebuffer and draw 3D scene(spinning cube)
		glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
		glBindVertexArray(vaoCube);
		glEnable(GL_DEPTH_TEST);
		glUseProgram(sceneShaderProgram);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texKitten);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texPuppy);

		// Clear the screen to white
		glClearColor(.2f, .2f, .2f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		GLfloat time = (GLfloat)glfwGetTime();
		glUniform1f(timer, time);
		time = 0;
		viewMatrix = camera.GetViewMatrix();
		projMatrix = glm::perspective(camera.Zoom, (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f);
		glm::mat4 modelMatrix;
		modelMatrix = glm::rotate(modelMatrix, time * 100, glm::vec3(0.0f, 0.0f, 1.0f));
		glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(modelMatrix));
		glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(viewMatrix));
		glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(projMatrix));

		// CUBE
		glDrawArrays(GL_TRIANGLES, 0, 36);

		glEnable(GL_STENCIL_TEST);
			// FLOOR
			/*glStencilFunc(GL_ALWAYS, 1, 0xFF);
			glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
			glStencilMask(0xFF);
			glDepthMask(GL_FALSE);
			glClear(GL_STENCIL_BUFFER_BIT);

			glDrawArrays(GL_TRIANGLES, 36, 6);
			
			// CUBE REFLECTION
			glStencilFunc(GL_EQUAL, 1, 0xFF);
			glStencilMask(0x00);
			glDepthMask(GL_TRUE);

			modelMatrix = glm::scale(glm::translate(modelMatrix, glm::vec3(0, 0, -1)), glm::vec3(1, 1, -1));
			glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(modelMatrix));

			glUniform3f(uniColor, 0.3f, 0.3f, 0.3f);

			glUniform3f(uniColor, 0.3f, 0.3f, 0.3f);
				glDrawArrays(GL_TRIANGLES, 0, 36);
			glUniform3f(uniColor, 1.0f, 1.0f, 1.0f);*/
		glDisable(GL_STENCIL_TEST);

		// Bind default framebuffer and draw contents of our framebuffer
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glBindVertexArray(vaoQuad);
		glDisable(GL_DEPTH_TEST);
		glUseProgram(screenShaderProgram);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texColorBuffer);

		glDrawArrays(GL_TRIANGLES, 0, 6);

		glfwSwapBuffers(window);
	}

	glDeleteRenderbuffers(1, &rboDepthStencil);
	glDeleteTextures(1, &texColorBuffer);
	glDeleteFramebuffers(1, &frameBuffer);

	glDeleteTextures(1, &texKitten);
	glDeleteTextures(1, &texPuppy);

	glDeleteProgram(screenShaderProgram);
	glDeleteShader(screenFragmentShader);
	glDeleteShader(screenVertexShader);

	glDeleteProgram(sceneShaderProgram);
	glDeleteShader(sceneFragmentShader);
	glDeleteShader(sceneVertexShader);

	glDeleteBuffers(1, &vboCube);
	glDeleteBuffers(1, &vboQuad);

	glDeleteVertexArrays(1, &vaoCube);
	glDeleteVertexArrays(1, &vaoQuad);

	glfwTerminate();
}
Пример #3
0
int main(int argc, char *argv[])
{
	auto t_start = std::chrono::high_resolution_clock::now();
	GLfloat directionx, directiony, directionz;
	directionx = 0.0f;
	directiony = 0.0f;
	directionz = 1.0f;
	GLboolean quit = GL_FALSE;
	GLenum errorValue;

	// Initialize SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		std::cerr << "SDL video initialization failed! Error: " << SDL_GetError() << std::endl;
		quit = GL_TRUE;
		SDL_Delay(5000);
	}
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
	SDL_Window* window = SDL_CreateWindow("OpenGL tutorial", 100, 100, width, height, SDL_WINDOW_OPENGL);
	SDL_GLContext context = SDL_GL_CreateContext(window);

	// Initialize GLEW
	glewExperimental = GL_TRUE;
	glewInit();
	
	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after GLEW init: " << errorValue << std::endl;
	}
	
	// Create Vertex Array Objects, don't bind yet
	/*
	VAOs are used to store all of the links between the vertex attributes and VBOs with raw vertex data.
	Since VBOs are just containers for the raw data that the graphics card reads and manipulates,
	the meaning and usage of the data has to be specified for each VBO & shader program.
	This might be quite cumbersome, if many shader programs are used, since the layout of the attributes would
	have to be specified every time. This is wheree VAOs come in: they can be used to store the links between
	VBO and the specified attributes. This way, a new VAO can be bound for each different shader program,
	which can then be changed easily at will by just calling glUseProgram(shaderProg1);.
	The graphics card then knows how to use the raw data in the VBO, since its usage and meaning has been
	specified beforehand and the links between VBOs and attributes has been saved to the VAO.

	As soon as VAO has been bound, all glVertexAttribPointer calls store the information to that VAO.
	*/
	GLuint vaoCube, vaoQuad;
	glGenVertexArrays(1, &vaoCube);
	glGenVertexArrays(1, &vaoQuad);

	// Create a Vertex Buffer Objects and upload vertex data
	/*
	VBOs are used to upload the vertex data to the graphics card.
	glGenBuffers(); creates a VBO, which can then be made active by binding it
	with glBindBuffer();. When the VBO has been set as active by binding it,
	the vertex data can be loaded to it with glBufferData();.

	Note that VBO/OpenGL doesn't know what the data means or is used for, it's just raw data.
	The usage of the data has to be specified, meaning which indices in the data correspond to which
	attribute (pos, color, texcoord, etc.).
	*/
	GLuint vboCube, vboQuad;
	glGenBuffers(1, &vboCube);
	glGenBuffers(1, &vboQuad);

	glBindBuffer(GL_ARRAY_BUFFER, vboCube);
	glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vboQuad);
	glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);

	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after VBOs: " << errorValue << std::endl;
	}

	// Generate shader programs
	GLuint sceneVertexShader, sceneFragmentShader, sceneShaderProgram;
	createShaderprogram(sceneVertexSource, sceneFragmentSource, sceneVertexShader, sceneFragmentShader, sceneShaderProgram);

	GLuint screenVertexShader, screenFragmentShader, screenShaderProgram;
	createShaderprogram(screenVertexSource, screenFragmentSource, screenVertexShader, screenFragmentShader, screenShaderProgram);

	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after generating shader programs: " << errorValue << std::endl;
	}

	// Specify the layout of the vertex data (bind vertex arrays)
	/*
	Since OpenGL doesn't know how the attributes of the vertices are
	specified in the arrays containing the vertex information (position, color, texture coordinates),
	they need to be specified by the user.
	Each attribute also saves the VBO that's been bound to the current GL_ARRAY_BUFFER.
	This means different VBOs can be used for each attribute if so desired.

	Only calls after a VAO has been bound will "stick" to it.
	As soon as VAO has been bound, all glVertexAttribPointer calls store the information to that VAO.
	See more complete explanation of VAO above.
	*/
	glBindVertexArray(vaoCube);
	glBindBuffer(GL_ARRAY_BUFFER, vboCube);
	specifySceneVertexAttributes(sceneShaderProgram);

	glBindVertexArray(vaoQuad);
	glBindBuffer(GL_ARRAY_BUFFER, vboQuad);
	specifyScreenVertexAttributes(screenShaderProgram);

	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after specifying the layout of vertex data: " << errorValue << std::endl;
	}

	// Load textures
	GLuint texKitten = loadTexture("sample.png");
	GLuint texPuppy = loadTexture("sample2.png");

	glUseProgram(sceneShaderProgram);
	glUniform1i(glGetUniformLocation(sceneShaderProgram, "texKitten"), 0);
	glUniform1i(glGetUniformLocation(sceneShaderProgram, "texPuppy"), 1);

	glUseProgram(screenShaderProgram);
	glUniform1i(glGetUniformLocation(screenShaderProgram, "texFramebuffer"), 0);

	GLint uniModel = glGetUniformLocation(sceneShaderProgram, "model");

	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after loading textures: " << errorValue << std::endl;
	}

	// Create frame buffer
	GLuint frameBuffer;
	glGenFramebuffers(1, &frameBuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after creating a frame buffer: " << errorValue << std::endl;
	}

	// Create texture to hold color buffer
	GLuint texColorBuffer;
	glGenTextures(1, &texColorBuffer);
	glBindTexture(GL_TEXTURE_2D, texColorBuffer);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);

	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after color buffer texture creation: " << errorValue << std::endl;
	}

	// Create Renderbuffer Object to hold depth and stencil buffers
	GLuint rboDepthStencil;
	glGenRenderbuffers(1, &rboDepthStencil);
	glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboDepthStencil);


	// Error checking
	errorValue = glGetError();
	if (errorValue != 0)
	{
		std::cerr << "GL_Error after RBO: " << errorValue << std::endl;
	}

	/*
	Set up projection:
	View matrix:
	First vector gives the position of the camera
	Second vector gives the point where the camera is centered or looking at
	Third vector defines "up", here up is the z-axis, xy-plane is the "ground"
	*/
	glm::mat4 view = glm::lookAt(
		glm::vec3(2.5f, 2.5f, 2.0f),
		glm::vec3(0.0f, 0.0f, 0.0f),
		glm::vec3(0.0f, 0.0f, 1.0f)
		);

	glUseProgram(sceneShaderProgram);

	GLint uniView = glGetUniformLocation(sceneShaderProgram, "view");
	glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));

	// Projection matrix
	glm::mat4 proj = glm::perspective(glm::radians(45.0f), GLfloat(width) / GLfloat(height), 1.0f, 10.0f);
	GLint uniProj = glGetUniformLocation(sceneShaderProgram, "proj");
	glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));

	GLint uniColor = glGetUniformLocation(sceneShaderProgram, "overrideColor");

	// Final error check will write errors (if any are encountered at this point) to file
	GLint status, status2, status3, status4, OpenGLError;
	OpenGLError = glGetError();
	GLenum frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	glGetShaderiv(sceneVertexShader, GL_COMPILE_STATUS, &status);
	glGetShaderiv(sceneFragmentShader, GL_COMPILE_STATUS, &status2);
	glGetShaderiv(screenVertexShader, GL_COMPILE_STATUS, &status3);
	glGetShaderiv(screenFragmentShader, GL_COMPILE_STATUS, &status4);

	if (
		OpenGLError != 0
		|| status == 0
		|| status2 == 0
		|| status3 == 0
		|| status4 == 0
		|| frameBufferStatus != GL_FRAMEBUFFER_COMPLETE)
	{
		char buffer1[512], buffer2[512], buffer3[512], buffer4[512];
		glGetShaderInfoLog(sceneVertexShader, 512, NULL, buffer1);
		glGetShaderInfoLog(sceneFragmentShader, 512, NULL, buffer2);
		glGetShaderInfoLog(screenVertexShader, 512, NULL, buffer3);
		glGetShaderInfoLog(screenFragmentShader, 512, NULL, buffer4);

		std::ofstream errorOutput;
		GLchar* errorOutFilename = "shader_errors.txt";
		errorOutput.open(errorOutFilename);
		
		errorOutput
			<< "GL_Error: " << OpenGLError
			<< "\n Scene vertex shader status: " << status
			<< "\n Scene vertex shader log: " << buffer1
			<< "\n Scene fragment shader status: " << status2
			<< "\n Scene fragment shader log: " << buffer2
			<< "\n Screen vertex shader status: " << status3
			<< "\n Screen vertex shader log: " << buffer3
			<< "\n Screen fragment shader status: " << status4
			<< "\n Screen fragment shader log: " << buffer4
			<< "\n Frame buffer status: " << frameBufferStatus
			<< "\n OpenGL version: " << glGetString(GL_VERSION);
		
		errorOutput.close();
		
		std::cerr << "An error has occured with shaders or frame buffer! See "
			<< errorOutFilename
			<< " for more info!\n Terminating program in 10s!"
			<< std::endl;

		SDL_Delay(10000);
		quit = GL_TRUE;
	}

	SDL_Event e;

	while (!quit)
	{
		while (SDL_PollEvent(&e) != 0)
		{
			if (e.type == SDL_QUIT)
			{
				quit = GL_TRUE;
			}
			else if (e.type == SDL_KEYDOWN)
			{
				switch (e.key.keysym.sym)
				{
				case SDLK_RETURN:
					directionx = 1.0f;
					directiony = 0.0f;
					directionz = 0.0f;
					break;
				case SDLK_SPACE:
					directionx = 0.0f;
					directiony = 1.0f;
					directionz = 0.0f;
					break;
				case SDLK_LCTRL:
					directionx = 0.0f;
					directiony = 0.0f;
					directionz = 1.0f;
				default:
					directionx = 0.0f;
					directiony = 0.0f;
					directionz = 1.0f;
					break;
				}
			}
		}

		// Bind framebuffer and draw 3D scene
		glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
		glBindVertexArray(vaoCube);
		glEnable(GL_DEPTH_TEST);
		glUseProgram(sceneShaderProgram);
		
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texKitten);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texPuppy);

		// Clear screen to greenish
		glClearColor(0.15f, 0.7f, 0.5f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Calculate transformation
		auto t_now = std::chrono::high_resolution_clock::now();
		float time = std::chrono::duration_cast<std::chrono::duration<float>>(t_now - t_start).count();
		
		glm::mat4 model;
		model = glm::rotate(model, time * glm::radians(180.f), glm::vec3(directionx, directiony, directionz));
		glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));

		// Draw cube
		glDrawArrays(GL_TRIANGLES, 0, 36);

		glEnable(GL_STENCIL_TEST);

		// Draw floor
		glStencilFunc(GL_ALWAYS, 1, 0xFF); // Set any stencil to 1
		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
		glStencilMask(0xFF); // Write to stencil buffer
		glDepthMask(GL_FALSE); // Don't write to depth buffer
		glClear(GL_STENCIL_BUFFER_BIT); // Clear stencil buffer (0 by default)

		glDrawArrays(GL_TRIANGLES, 36, 6);

		// Draw cube reflection

		glStencilFunc(GL_EQUAL, 1, 0xFF); // Pass test if stencil value is 1
		glStencilMask(0x00); // Don't write anything to stencil buffer
		glDepthMask(GL_TRUE); // Write to depth buffer

		model = glm::scale(glm::translate(model, glm::vec3(0, 0, -1)), glm::vec3(1, 1, -1));
		glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));

		glUniform3f(uniColor, 0.3f, 0.3f, 0.3f);
		glDrawArrays(GL_TRIANGLES, 0, 36);
		glUniform3f(uniColor, 1.0f, 1.0f, 1.0f);

		glDisable(GL_STENCIL_TEST);

		// Bind default framebuffer and draw contents of our framebuffer
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glBindVertexArray(vaoQuad);
		glDisable(GL_DEPTH_TEST);
		glUseProgram(screenShaderProgram);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texColorBuffer);

		glDrawArrays(GL_TRIANGLES, 0, 6);

		// Swap buffers
		SDL_GL_SwapWindow(window);
		
	}
	glDeleteRenderbuffers(1, &rboDepthStencil);
	glDeleteTextures(1, &texColorBuffer);
	glDeleteFramebuffers(1, &frameBuffer);
	
	glDeleteTextures(1, &texKitten);
	glDeleteTextures(1, &texPuppy);

	glDeleteProgram(screenShaderProgram);
	glDeleteShader(screenFragmentShader);
	glDeleteShader(screenVertexShader);

	glDeleteProgram(sceneShaderProgram);
	glDeleteShader(sceneFragmentShader);
	glDeleteShader(sceneVertexShader);

	glDeleteBuffers(1, &vboCube);
	glDeleteBuffers(1, &vboQuad);

	glDeleteVertexArrays(1, &vaoCube);
	glDeleteVertexArrays(1, &vaoQuad);

	SDL_GL_DeleteContext(context);
	SDL_Quit();
	return 0;
}