This image was captured with a Renderer of size (200 x 200).
You can see a very "pixeled" image, thats because the Framebuffer of the renderer was copied with the filter parameter "GL_NEAREST".
This image was also captured with a Renderer of size (200 x 200).
Here you cant see the pixels anymore, thats because the Framebuffer of the renderer was copied with the filter parameter "GL_LINEAR".
The function that copies our framebuffer into the default framebuffer (window framebuffer) is:
glBlitFramebuffer(
0, 0, m_width, m_height, // source area
0, 0, windowsize.x, windowsize.y, // destination area
GL_COLOR_BUFFER_BIT, // buffer bitmask
GL_NEAREST); // filter parameter
Complete Source Code:
Main.cpp
...
int Main::MainLoop()
{
double tnow = 0, tframe = 0, tlastframe = 0;
unsigned int tlastrenamed = 0, fpscounter = 0;
ivec2 windowsize = Framework->WindowSize();
m_renderer = std::unique_ptr<Renderer>(new Renderer_OpenGL(windowsize.x, windowsize.y));
Scene scene;
while (!Framework->IsCloseRequested())
{
// limit the FPS
tnow = Framework->Time();
tframe = tnow - tlastframe;
if (tframe < 1.0 / m_fps_goal)
continue;
tlastframe = tnow;
// rename the window title each second, show current FPS
fpscounter++;
if ((unsigned int)tnow != tlastrenamed)
{
tlastrenamed = (unsigned int)tnow;
Framework->SetWindowTitle("OpenGL FPS: " + std::to_string(fpscounter));
fpscounter = 0;
}
// simulate scene
static int tracked_ID = 0;
scene.AnimateNextFrame(tframe, tracked_ID);
// render scene
if (m_renderer.get())
m_renderer->Render(scene, tracked_ID);
Framework->SwapBuffers();
Framework->Update();
}
return EXIT_SUCCESS;
}
...
Renderer_OpenGL.cpp
#include "Renderer_OpenGL.h"
#include "Shader.h"
#include "Model.h"
#include "OBJ.h"
#include "Main.h"
#include <iostream>
#define MAX_MODEL_INSTANCES 1000
void CheckForGLError()
{
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
{
std::cout << "ERROR: (Renderer_OpenGL) \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_INVALID_FRAMEBUFFER_OPERATION)
std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_OUT_OF_MEMORY)
std::cout << "GL_OUT_OF_MEMORY";
if (error == GL_STACK_UNDERFLOW)
std::cout << "GL_STACK_UNDERFLOW";
if (error == GL_STACK_OVERFLOW)
std::cout << "GL_STACK_OVERFLOW";
std::cout << (char)7 << std::endl; /*play sound*/
std::cin.get();
}
}
Renderer_OpenGL::Renderer_OpenGL(unsigned int width, unsigned int height)
{
// settings
// ----------------------------------------------------------------------------------------------------------
glEnable(GL_DEPTH_TEST); // 3D
glEnable(GL_CULL_FACE); // dont render backward faces
// ----------------------------------------------------------------------------------------------------------
// setup framebuffer:
// ----------------------------------------------------------------------------------------------------------
// make sure that the framebuffer isnt too small
m_width = max2(100, width);
m_height = max2(100, height);
glViewport(0, 0, m_width, m_height);
glGenTextures(1, &m_framebuffercolortexture);
glBindTexture(GL_TEXTURE_2D, m_framebuffercolortexture);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferintegertexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferintegertexture);
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);
// special texture to store only integer values (32 bit)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, m_width, m_height, 0, GL_RED_INTEGER, GL_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferdepthtexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferdepthtexture);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create framebuffer
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_framebuffercolortexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_framebufferintegertexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_framebufferdepthtexture, 0);
std::vector<GLenum> drawbuffers = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(drawbuffers.size(), drawbuffers.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ERROR CHECK:
GLenum status = glCheckNamedFramebufferStatus(m_framebuffer, GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (status == GL_FRAMEBUFFER_UNDEFINED)
std::cout << "ERROR: undefined framebuffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
std::cout << "ERROR: a necessary attachment is uninitialized" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
std::cout << "ERROR: no attachments" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)
std::cout << "ERROR: incomplete draw buffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
std::cout << "ERROR: incomplete read buffer" << std::endl;
if (status == GL_FRAMEBUFFER_UNSUPPORTED)
std::cout << "ERROR: combination of attachments is not supported" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)
std::cout << "ERROR: number if samples for all attachments does not match" << std::endl;
}
// ----------------------------------------------------------------------------------------------------------
// setup program
// ----------------------------------------------------------------------------------------------------------
m_program = glCreateProgram();
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);
// ----------------------------------------------------------------------------------------------------------
// setup vertexarray
// ----------------------------------------------------------------------------------------------------------
glGenVertexArrays(1, &m_vertexarray);
glGenBuffers(1, &m_vertexbuffer);
glGenBuffers(1, &m_instancebuffer);
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));
glVertexAttribPointer(1, 4, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_instancebuffer);
glVertexAttribPointer(2, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 0));
glVertexAttribPointer(3, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 4));
glVertexAttribPointer(4, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 8));
glVertexAttribPointer(5, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 12));
glVertexAttribIPointer(6, 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);
// sent these attributes only once per instance to the program:
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glBindVertexArray(0);
// ----------------------------------------------------------------------------------------------------------
// setup vertex buffer
// ----------------------------------------------------------------------------------------------------------
OBJ model("monkey.obj", "");
if (!model.GetErrorString().empty())
std::cout << model.GetErrorString() << std::endl;
std::vector<Vertex> vertices;
for (auto& object : model.Objects)
{
for (auto& groupmember : object.second.Groups)
{
for (auto& facemap : groupmember.second.Faces)
{
for (auto& facetype : facemap.second)
{
// only process triangles:
if (facetype.first == 3)
{
for (auto& face : facetype.second)
{
vec3 position, normal;
position = model.Positions[face.Points[0].Index_v - 1];
vertices.push_back({ position,{ 1, 0, 0, 1 } });
position = model.Positions[face.Points[1].Index_v - 1];
vertices.push_back({ position,{ 0, 1, 0, 1 } });
position = model.Positions[face.Points[2].Index_v - 1];
vertices.push_back({ position,{ 0, 0, 1, 1 } });
}
}
}
}
}
}
if (vertices.size() == 0)
{
vertices.push_back({ { 0, 0, 0 },{ 1, 0, 0, 1 } });
vertices.push_back({ { 1, 0, 0 },{ 0, 1, 0, 1 } });
vertices.push_back({ { 0, 1, 0 },{ 0, 0, 1, 1 } });
}
m_vertexcount = vertices.size();
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
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_instancebuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ModelInstance) * MAX_MODEL_INSTANCES, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
}
Renderer_OpenGL::~Renderer_OpenGL()
{
glDeleteProgram(m_program);
glDeleteFramebuffers(1, &m_framebuffer);
glDeleteTextures(1, &m_framebuffercolortexture);
glDeleteTextures(1, &m_framebufferintegertexture);
glDeleteTextures(1, &m_framebufferdepthtexture);
glDeleteVertexArrays(1, &m_vertexarray);
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_instancebuffer);
}
void Renderer_OpenGL::Render(const Scene & scene, int& tracked_ID)
{
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
// explicitly clear each buffer:
float clearvalue0[] = { 0.0f, 0.5f, 0.0f, 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);
glUseProgram(m_program);
glBindVertexArray(m_vertexarray);
// set camera
glUniformMatrix4fv(glGetUniformLocation(m_program, "View"), 1, false, value_ptr(scene.Camera.View()));
glUniformMatrix4fv(glGetUniformLocation(m_program, "Projection"), 1, false, value_ptr(perspective(scene.Camera.FoV, 1.33f, scene.Camera.ZNear, scene.Camera.ZFar)));
// create instance buffer data:
std::vector<ModelInstance> instances;
for (auto& object : scene.Objects)
instances.push_back({ object.ModelMatrix(), (int)object.ID() });
// determine instance count:
unsigned int instancecount = instances.size();
if (instancecount >= MAX_MODEL_INSTANCES)
instancecount = MAX_MODEL_INSTANCES;
// upload instance buffer data:
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ModelInstance) * instancecount, instances.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// render objects in scene
glDrawArraysInstanced(GL_TRIANGLES, 0, m_vertexcount, instancecount);
glBindVertexArray(0);
glUseProgram(0);
// 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);
// 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);
// check for errors
CheckForGLError();
}