Tutorial 08.1: Forward Renderer

We already have build a "Forward Renderer" in the previous tutorials.

Forward Rendering means that we render the scene once, and during that process we do all the shading.

Another approach would be "Deferred Rendering" which means that we defer the shading process, using different shading programs.

Here we just re-structure the code a little bit.

Complete Source Code:

Renderer_OpenGL.h

#pragma once

#include "Renderer.h"

#include <GL/glew.h>

class Renderer_OpenGL : public Renderer

{

public:

Renderer_OpenGL(unsigned int width, unsigned int height);

virtual ~Renderer_OpenGL();

virtual void Render(const Scene& scene, int& tracked_ID);

protected:

unsigned int m_width{ 0 }, m_height{ 0 };

enum Buffers {

BufferVertices,

BufferInstances,

BufferMaterials,

BufferDirectionalLights,

BufferPointLights,

BufferSpotLights,

MAX_Buffers,

};

enum FramebufferTexture {

FramebufferColor,

FramebufferObjectID,

FramebufferDepthStencil,

MAX_FramebufferTextures,

};

unsigned int m_framebuffer{ 0 };

unsigned int m_framebuffertextures[MAX_FramebufferTextures]{ 0 };

unsigned int m_program{ 0 };

unsigned int m_vertexarray{ 0 };

unsigned int m_buffers[MAX_Buffers]{ 0 };

unsigned int m_textureKd{ 0 };

std::vector<Model> m_models;

// uniform locations

struct {

struct {

int View = -1;

int Projection = -1;

int CameraPosition = -1;

int Ambient = -1;

int DirectionalLightCount = -1;

int PointLightCount = -1;

int SpotLightCount = -1;

int textureKd = -1;

} program;

} m_uniform_location;

void CheckFramebuffer(unsigned int framebuffer);

void CheckForGLError();

};

Renderer_OpenGL.cpp

#include "Renderer_OpenGL.h"

#include "Shader.h"

#include "Model.h"

#include "BMP.h"

#include "Main.h"

#include <iostream>

#define MAX_MODEL_INSTANCES 1000

#define MAX_MATERIALS 1000

#define MAX_POINTLIGHTS 10

#define MAX_DIRECTIONALLIGHTS 10

#define MAX_SPOTLIGHTS 10

Renderer_OpenGL::Renderer_OpenGL(unsigned int width, unsigned int height)

{

// settings

//-----------------------------------------------------------------------------------------------------

glEnable(GL_DEPTH_TEST);

glEnable(GL_CULL_FACE);

//-----------------------------------------------------------------------------------------------------

// create all objects

//-----------------------------------------------------------------------------------------------------

glGenFramebuffers(1, &m_framebuffer);

glGenTextures(MAX_FramebufferTextures, m_framebuffertextures);

m_program = glCreateProgram();

glGenVertexArrays(1, &m_vertexarray);

glGenBuffers(MAX_Buffers, m_buffers);

glGenTextures(1, &m_textureKd);

//-----------------------------------------------------------------------------------------------------

// framebuffer size

//-----------------------------------------------------------------------------------------------------

m_width = max2(100, width);

m_height = max2(100, height);

glViewport(0, 0, m_width, m_height);

//-----------------------------------------------------------------------------------------------------

// setup framebuffer

//-----------------------------------------------------------------------------------------------------

for (unsigned int i = 0; i < MAX_FramebufferTextures; i++)

{

glBindTexture(GL_TEXTURE_2D, m_framebuffertextures[i]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glBindTexture(GL_TEXTURE_2D, 0);

}

glBindTexture(GL_TEXTURE_2D, m_framebuffertextures[FramebufferColor]);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

glBindTexture(GL_TEXTURE_2D, m_framebuffertextures[FramebufferObjectID]);

glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, m_width, m_height, 0, GL_RED_INTEGER, GL_INT, NULL);

glBindTexture(GL_TEXTURE_2D, m_framebuffertextures[FramebufferDepthStencil]);

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

glBindTexture(GL_TEXTURE_2D, 0);

glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);

glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_framebuffertextures[FramebufferColor], 0);

glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_framebuffertextures[FramebufferObjectID], 0);

glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_framebuffertextures[FramebufferDepthStencil], 0);

std::vector<GLenum> drawbuffers = {

GL_COLOR_ATTACHMENT0,

GL_COLOR_ATTACHMENT1,

};

glDrawBuffers(drawbuffers.size(), drawbuffers.data());

glBindFramebuffer(GL_FRAMEBUFFER, 0);

CheckFramebuffer(m_framebuffer);

//-----------------------------------------------------------------------------------------------------

// setup program

//-----------------------------------------------------------------------------------------------------

unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);

unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

CompileShader(vertexshader, LoadTextFile("shader.vs"));

CompileShader(fragmentshader, LoadTextFile("shader.fs"));

LinkProgram(m_program, { vertexshader , fragmentshader });

glDeleteShader(vertexshader);

glDeleteShader(fragmentshader);

// get all uniform locations (program)

m_uniform_location.program.View = glGetUniformLocation(m_program, "View");

m_uniform_location.program.Projection = glGetUniformLocation(m_program, "Projection");

m_uniform_location.program.CameraPosition = glGetUniformLocation(m_program, "CameraPosition");

m_uniform_location.program.Ambient = glGetUniformLocation(m_program, "Ambient");

m_uniform_location.program.DirectionalLightCount = glGetUniformLocation(m_program, "DirectionalLightCount");

m_uniform_location.program.PointLightCount = glGetUniformLocation(m_program, "PointLightCount");

m_uniform_location.program.SpotLightCount = glGetUniformLocation(m_program, "SpotLightCount");

m_uniform_location.program.textureKd = glGetUniformLocation(m_program, "textureKd");

//-----------------------------------------------------------------------------------------------------

// setup vertexarray

//-----------------------------------------------------------------------------------------------------

glBindVertexArray(m_vertexarray);

glBindBuffer(GL_ARRAY_BUFFER, m_buffers[BufferVertices]);

glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));

glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));

glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 5));

glVertexAttribIPointer(3, 1, GL_INT, sizeof(Vertex), (void*)(sizeof(float) * 8));

glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindBuffer(GL_ARRAY_BUFFER, m_buffers[BufferInstances]);

glVertexAttribPointer(4, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 0));

glVertexAttribPointer(5, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 4));

glVertexAttribPointer(6, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 8));

glVertexAttribPointer(7, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 12));

glVertexAttribIPointer(8, 1, GL_INT, sizeof(ModelInstance), (void*)(sizeof(float) * 16));

glBindBuffer(GL_ARRAY_BUFFER, 0);

glEnableVertexAttribArray(0);

glEnableVertexAttribArray(1);

glEnableVertexAttribArray(2);

glEnableVertexAttribArray(3);

glEnableVertexAttribArray(4);

glEnableVertexAttribArray(5);

glEnableVertexAttribArray(6);

glEnableVertexAttribArray(7);

glEnableVertexAttribArray(8);

// sent these attributes only once per instance to the program:

glVertexAttribDivisor(4, 1);

glVertexAttribDivisor(5, 1);

glVertexAttribDivisor(6, 1);

glVertexAttribDivisor(7, 1);

glVertexAttribDivisor(8, 1);

glBindVertexArray(0);

//-----------------------------------------------------------------------------------------------------

// setup vertex buffer

//-----------------------------------------------------------------------------------------------------

std::vector<Vertex> vertices;

std::vector<Material> materials;

std::vector<std::string> texturesdiffuse = { "" };

// list of all model filenames + base paths

std::list<std::pair<std::string, std::string>> listmodelfiles;

listmodelfiles.push_back({ { "sphere.obj" },{ "" } });

listmodelfiles.push_back({ { "monkey.obj" },{ "" } });

// create models:

for (auto& modelfile : listmodelfiles)

{

Model model;

model.Primitive = GL_TRIANGLES;

model.Offset = vertices.size();

LoadModel(modelfile.first, modelfile.second, vertices, materials, texturesdiffuse);

model.Count = vertices.size() - model.Offset;

// check if model has any vertex data

if (model.Count < 3)

continue;

// now set the size information of this model:

float rmax_sq = 0;

for (unsigned int i = model.Offset; i < model.Offset + model.Count; i++)

{

model.Size.Min.x = min2(model.Size.Min.x, vertices[i].Position.x);

model.Size.Min.y = min2(model.Size.Min.y, vertices[i].Position.y);

model.Size.Min.z = min2(model.Size.Min.z, vertices[i].Position.z);

model.Size.Max.x = max2(model.Size.Max.x, vertices[i].Position.x);

model.Size.Max.y = max2(model.Size.Max.y, vertices[i].Position.y);

model.Size.Max.z = max2(model.Size.Max.z, vertices[i].Position.z);

rmax_sq = max2(rmax_sq, dot(vertices[i].Position, vertices[i].Position));

}

model.Size.RMax = std::sqrt(rmax_sq);

// append model

m_models.push_back(model);

}

// show all model infos:

for (auto& model : m_models)

{

static int index = 0;

std::cout << "model " << index++ << std::endl;

std::cout << "x: " << model.Size.Min.x << " ... " << model.Size.Max.x << std::endl;

std::cout << "y: " << model.Size.Min.y << " ... " << model.Size.Max.y << std::endl;

std::cout << "z: " << model.Size.Min.z << " ... " << model.Size.Max.z << std::endl;

std::cout << "r max: " << model.Size.RMax << std::endl;

std::cout << "vertex count: " << model.Count << std::endl << std::endl;

}

// make sure that there is at least 1 model available:

if (vertices.size() == 0)

{

vertices.push_back({ { 0, 0, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });

vertices.push_back({ { 1, 0, 0 },{ 0, 1 },{ 0, 0, 1 },{ 0 } });

vertices.push_back({ { 0, 1, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });

Model model;

model.Primitive = GL_TRIANGLES;

model.Offset = 0;

model.Count = 3;

m_models.push_back(model);

}

glBindBuffer(GL_ARRAY_BUFFER, m_buffers[BufferVertices]);

glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);

//-----------------------------------------------------------------------------------------------------

// setup instance buffer

//-----------------------------------------------------------------------------------------------------

glBindBuffer(GL_UNIFORM_BUFFER, m_buffers[BufferInstances]);

glBufferData(GL_UNIFORM_BUFFER, sizeof(ModelInstance) * MAX_MODEL_INSTANCES, NULL, GL_STREAM_DRAW);

glBindBuffer(GL_UNIFORM_BUFFER, 0);

//-----------------------------------------------------------------------------------------------------

// setup material and light buffers

//-----------------------------------------------------------------------------------------------------

if (materials.size() == 0)

materials.push_back(Material(vec3(1, 1, 1), vec3(1, 1, 1), vec3(1, 1, 1), 1.0f, 1.0f, 0));

materials.resize(MAX_MATERIALS);

glBindBufferBase(GL_UNIFORM_BUFFER, 1, m_buffers[BufferMaterials]);

glBufferData(GL_UNIFORM_BUFFER, sizeof(Material) * materials.size(), materials.data(), GL_STATIC_DRAW);

glBindBufferBase(GL_UNIFORM_BUFFER, 2, m_buffers[BufferDirectionalLights]);

glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLight) * MAX_DIRECTIONALLIGHTS, NULL, GL_STREAM_DRAW);

glBindBufferBase(GL_UNIFORM_BUFFER, 3, m_buffers[BufferPointLights]);

glBufferData(GL_UNIFORM_BUFFER, sizeof(PointLight) * MAX_POINTLIGHTS, NULL, GL_STREAM_DRAW);

glBindBufferBase(GL_UNIFORM_BUFFER, 4, m_buffers[BufferSpotLights]);

glBufferData(GL_UNIFORM_BUFFER, sizeof(SpotLight) * MAX_SPOTLIGHTS, NULL, GL_STREAM_DRAW);

glBindBuffer(GL_UNIFORM_BUFFER, 0);

//-----------------------------------------------------------------------------------------------------

// setup textures

//-----------------------------------------------------------------------------------------------------

unsigned int mipmapcount = 1;

unsigned int texturewidth = 1024;

unsigned int textureheight = 1024;

unsigned int numberoflayers = texturesdiffuse.size();

struct Pixel { unsigned char r{ 255 }, g{ 255 }, b{ 255 }, a{ 255 }; };

std::vector<Pixel> whitetexture(texturewidth * textureheight);

glBindTexture(GL_TEXTURE_2D_ARRAY, m_textureKd);

glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipmapcount, GL_RGBA8, texturewidth, textureheight, numberoflayers);

for (unsigned int i = 0; i < texturesdiffuse.size(); i++)

{

// this c++ class to load images was written by Benjamin Kalytta, http://www.kalytta.com/bitmap.h

CBitmap bmp;

void* texturedata = whitetexture.data();

if (bmp.Load(texturesdiffuse[i].c_str()))

if (bmp.GetWidth() == texturewidth && bmp.GetHeight() == textureheight)

texturedata = bmp.GetBits();

glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, texturewidth, textureheight, 1, GL_RGBA, GL_UNSIGNED_BYTE, texturedata);

}

glBindTexture(GL_TEXTURE_2D_ARRAY, 0);

// bind diffuse texture to textureunit 1:

glBindTextureUnit(1, m_textureKd);

glProgramUniform1i(m_program, m_uniform_location.program.textureKd, 1);

//-----------------------------------------------------------------------------------------------------

}

Renderer_OpenGL::~Renderer_OpenGL()

{

// destroy all objects

//-----------------------------------------------------------------------------------------------------

glDeleteFramebuffers(1, &m_framebuffer);

glDeleteTextures(MAX_FramebufferTextures, m_framebuffertextures);

glDeleteProgram(m_program);

glDeleteVertexArrays(1, &m_vertexarray);

glDeleteBuffers(MAX_Buffers, m_buffers);

glDeleteTextures(1, &m_textureKd);

//-----------------------------------------------------------------------------------------------------

}

void Renderer_OpenGL::Render(const Scene & scene, int& tracked_ID)

{

// 0. update uniform variables and buffers

// 0.1 set camera

//-----------------------------------------------------------------------------------------------------

glProgramUniformMatrix4fv(m_program, m_uniform_location.program.View, 1, false, value_ptr(scene.Camera.View()));

glProgramUniformMatrix4fv(m_program, m_uniform_location.program.Projection, 1, false, value_ptr(perspective(scene.Camera.FoV, 1.33f, scene.Camera.ZNear, scene.Camera.ZFar)));

//-----------------------------------------------------------------------------------------------------

// 0.2 set light sources:

//-----------------------------------------------------------------------------------------------------

glProgramUniform3fv(m_program, m_uniform_location.program.CameraPosition, 1, value_ptr(scene.Camera.Position()));

glProgramUniform3fv(m_program, m_uniform_location.program.Ambient, 1, value_ptr(scene.Light.Ambient));

unsigned int directionallightcount = min2(MAX_DIRECTIONALLIGHTS, scene.Light.DirectionalLights.size());

unsigned int pointlightcount = min2(MAX_POINTLIGHTS, scene.Light.PointLights.size());

unsigned int spotlightcount = min2(MAX_SPOTLIGHTS, scene.Light.SpotLights.size());

glProgramUniform1i(m_program, m_uniform_location.program.DirectionalLightCount, directionallightcount);

glProgramUniform1i(m_program, m_uniform_location.program.PointLightCount, pointlightcount);

glProgramUniform1i(m_program, m_uniform_location.program.SpotLightCount, spotlightcount);

if (directionallightcount > 0)

{

glBindBuffer(GL_UNIFORM_BUFFER, m_buffers[BufferDirectionalLights]);

glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLight) * directionallightcount, scene.Light.DirectionalLights.data());

glBindBuffer(GL_UNIFORM_BUFFER, 0);

}

if (pointlightcount > 0)

{

glBindBuffer(GL_UNIFORM_BUFFER, m_buffers[BufferPointLights]);

glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(PointLight) * pointlightcount, scene.Light.PointLights.data());

glBindBuffer(GL_UNIFORM_BUFFER, 0);

}

if (spotlightcount > 0)

{

glBindBuffer(GL_UNIFORM_BUFFER, m_buffers[BufferSpotLights]);

glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SpotLight) * spotlightcount, scene.Light.SpotLights.data());

glBindBuffer(GL_UNIFORM_BUFFER, 0);

}

//-----------------------------------------------------------------------------------------------------

// 1. render into framebuffer

glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);

// 1.1 explicitly clear each buffer:

//-----------------------------------------------------------------------------------------------------

float clearvalue0[] = { 0.1f, 0.2f, 0.7f, 0.0f }; // background color

int clearvalue1[] = { -1 }; // background integer values

glClearBufferfv(GL_COLOR, 0, clearvalue0);

glClearBufferiv(GL_COLOR, 1, clearvalue1);

glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

//-----------------------------------------------------------------------------------------------------

// 1.2 activate program and vertexarray

//-----------------------------------------------------------------------------------------------------

glUseProgram(m_program);

glBindVertexArray(m_vertexarray);

// 1.3 draw scene

for (auto& object : scene.Objects)

if (object.ModelIndex < m_models.size())

m_models[object.ModelIndex].Instances.push_back({ object.ModelMatrix(), (int)object.ID() });

for (auto& model : m_models)

{

// determine instance count:

unsigned int instancecount = min2(model.Instances.size(), MAX_MODEL_INSTANCES);

// upload instance buffer data:

glBindBuffer(GL_ARRAY_BUFFER, m_buffers[BufferInstances]);

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ModelInstance) * instancecount, model.Instances.data());

glBindBuffer(GL_ARRAY_BUFFER, 0);

// render objects in scene

glDrawArraysInstanced(model.Primitive, model.Offset, model.Count, instancecount);

}

for (auto& model : m_models)

{

model.Instances.clear();

model.Instances.reserve(MAX_MODEL_INSTANCES);

}

glBindVertexArray(0);

glUseProgram(0);

//-----------------------------------------------------------------------------------------------------

// 1.4 read ID at cursor position:

//-----------------------------------------------------------------------------------------------------

ivec2 cursorposition = Framework->CursorPosition();

ivec2 windowsize = Framework->WindowSize();

int x = cursorposition.x * (float)m_width / windowsize.x;

int y = cursorposition.y * (float)m_height / windowsize.y;

glReadBuffer(GL_COLOR_ATTACHMENT1);

glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &tracked_ID);

//-----------------------------------------------------------------------------------------------------

// 2. copy to default framebuffer:

//-----------------------------------------------------------------------------------------------------

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);

glReadBuffer(GL_COLOR_ATTACHMENT0);

glBlitFramebuffer(

0, 0, m_width, m_height,

0, 0, windowsize.x, windowsize.y,

GL_COLOR_BUFFER_BIT,

GL_NEAREST);

//-----------------------------------------------------------------------------------------------------

CheckForGLError();

}

void Renderer_OpenGL::CheckFramebuffer(unsigned int framebuffer)

{

GLenum status = glCheckNamedFramebufferStatus(framebuffer, GL_FRAMEBUFFER);

if (status != GL_FRAMEBUFFER_COMPLETE)

{

std::cout << "OpenGL Framebuffer Error: \t";

if (status == GL_FRAMEBUFFER_UNDEFINED)

std::cout << "undefined framebuffer";

if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)

std::cout << "a necessary attachment is uninitialized";

if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)

std::cout << "no attachments";

if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)

std::cout << "incomplete draw buffer";

if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)

std::cout << "incomplete read buffer";

if (status == GL_FRAMEBUFFER_UNSUPPORTED)

std::cout << "combination of attachments is not supported";

if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)

std::cout << "number if samples for all attachments does not match";

std::cout << (char)7 << std::endl; /*play sound*/

std::cin.get();

}

}

void Renderer_OpenGL::CheckForGLError()

{

for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)

{

std::cout << "OpenGL Error: \t";

if (error == GL_INVALID_ENUM)

std::cout << "GL_INVALID_ENUM";

if (error == GL_INVALID_VALUE)

std::cout << "GL_INVALID_VALUE";

if (error == GL_INVALID_OPERATION)

std::cout << "GL_INVALID_OPERATION";

if (error == GL_STACK_OVERFLOW)

std::cout << "GL_STACK_OVERFLOW";

if (error == GL_STACK_UNDERFLOW)

std::cout << "GL_STACK_UNDERFLOW";

if (error == GL_OUT_OF_MEMORY)

std::cout << "GL_OUT_OF_MEMORY";

if (error == GL_INVALID_FRAMEBUFFER_OPERATION)

std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION";

if (error == GL_CONTEXT_LOST)

std::cout << "GL_CONTEXT_LOST";

std::cout << (char)7 << std::endl; /*play sound*/

std::cin.get();

}

}