3.1: Multisample

To enable multisampling we call:

glEnable(GL_MULTISAMPLE);

To create a texture attachment for the off-screen framebuffer, we call:

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebuffertexture);

glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, SCREENWIDTH, SCREENHEIGHT, GL_TRUE);

To attach this multisampled texture to our off-screen framebuffer, we call:

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, framebuffertexture, 0);

To copy the resultting image we rendered into the off-screen framebuffer onto the screen, we call:

// copy from off-screen framebuffer into default framebuffer (screen)

glBlitFramebuffer(

0, 0, SCREENWIDTH, SCREENHEIGHT,

0, 0, SCREENWIDTH, SCREENHEIGHT,

GL_COLOR_BUFFER_BIT, GL_NEAREST);

Note:

the attachments of the off-screen framebuffer have to have the same resolution as the screen, otherwise blitting the framebuffers isnt possible.

Complete Source Code:

Main.cpp

#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include <glm/glm.hpp>

#include <glm/gtc/type_ptr.hpp>

#include <glm/gtx/transform.hpp>

#include <glm/gtc/matrix_transform.hpp>

#include <glm/gtc/quaternion.hpp>

#include <glm/gtx/rotate_vector.hpp>

#include <iostream>

#include <vector>

#include "Shader.h"

using namespace std;

using namespace glm;

#define SCREENWIDTH 640

#define SCREENHEIGHT 480

// main functions

void Initialize();

void Render();

void CleanUp();

void CheckForGLError();

void CheckFramebuffer(GLuint framebuffer, GLenum target);

GLFWwindow* window = nullptr;

unsigned int multisample = 4;

GLuint framebuffer = 0;

GLuint framebuffertexture = 0;

GLuint program = 0;

GLuint vertexshader = 0;

GLuint fragmentshader = 0;

GLuint vertexarray = 0;

GLuint vertexbuffer = 0;

struct Vertex

{

vec3 Position;

vec4 Color;

};

int main(void)

{

/* Initialize the library */

if (!glfwInit())

return -1;

/* Create a windowed mode window and its OpenGL context */

window = glfwCreateWindow(SCREENWIDTH, SCREENHEIGHT, "Hello World", NULL, NULL);

if (!window)

{

glfwTerminate();

return -1;

}

/* Make the window's context current */

glfwMakeContextCurrent(window);

/* Initialize GLEW */

if (glewInit() != GLEW_OK)

{

glfwTerminate();

return -1;

}

Initialize();

/* Loop until the user closes the window */

while (!glfwWindowShouldClose(window))

{

/* Render here */

Render();

/* Swap front and back buffers */

glfwSwapBuffers(window);

/* Poll for and process events */

glfwPollEvents();

}

CleanUp();

glfwTerminate();

return 0;

}

void Initialize()

{

// background color

glClearColor(0.3f, 0.5f, 0.8f, 0.0f);

glEnable(GL_MULTISAMPLE);

// create all objects

glGenFramebuffers(1, &framebuffer);

glGenTextures(1, &framebuffertexture);

program = glCreateProgram();

vertexshader = glCreateShader(GL_VERTEX_SHADER);

fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

glGenVertexArrays(1, &vertexarray);

glGenBuffers(1, &vertexbuffer);

// setup framebuffer

glViewport(0, 0, SCREENWIDTH, SCREENHEIGHT);

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebuffertexture);

glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, SCREENWIDTH, SCREENHEIGHT, GL_TRUE);

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, framebuffertexture, 0);

glDrawBuffer(GL_COLOR_ATTACHMENT0);

glBindFramebuffer(GL_FRAMEBUFFER, 0);

CheckFramebuffer(framebuffer, GL_FRAMEBUFFER);

// shader source code

string vertexshader_source = {

"#version 450 core\n"

"in layout (location = 0) vec3 in_position;"

"in layout (location = 1) vec4 in_color;"

"uniform layout (location = 0) mat4 MVP = mat4(1);"

"out VS_FS_INTERFACE {"

"vec4 color;"

"} vs_out;"

"void main () {"

"gl_Position = MVP * vec4(in_position, 1);"

"vs_out.color = in_color;"

"}"

};

string fragmentshader_source = {

"#version 450 core\n"

"in VS_FS_INTERFACE {"

"vec4 color;"

"} vs_out;"

"out layout (location = 0) vec4 out_color;"

"void main () {"

"out_color = vs_out.color;"

"}"

};

// compile shaders and link program

CompileShader(vertexshader, vertexshader_source);

CompileShader(fragmentshader, fragmentshader_source);

LinkProgram(program, { vertexshader, fragmentshader });

// setup vertex array

glBindVertexArray(vertexarray);

glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(0));

glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(vec3)));

glBindBuffer(GL_ARRAY_BUFFER, 0);

glEnableVertexAttribArray(0);

glEnableVertexAttribArray(1);

glBindVertexArray(0);

// setup vertex buffer

vector<Vertex> vertices = {

{ { 0, 0, 0 },{ 1, 0, 0, 1 } },

{ { 1, 0, 0 },{ 0, 1, 0, 1 } },

{ { 0, 1, 0 },{ 0, 0, 1, 1 } },

};

glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

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

glBindBuffer(GL_ARRAY_BUFFER, 0);

}

void Render()

{

// render into the off-screen framebuffer

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

// clear the off-screen framebuffer

glClear(GL_COLOR_BUFFER_BIT);

// render a triangle

glUseProgram(program);

glBindVertexArray(vertexarray);

glDrawArrays(GL_TRIANGLES, 0, 3);

glBindVertexArray(0);

glUseProgram(0);

// render into the default framebuffer (screen)

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

// copy from off-screen framebuffer into default framebuffer (screen)

glBlitFramebuffer(

0, 0, SCREENWIDTH, SCREENHEIGHT,

0, 0, SCREENWIDTH, SCREENHEIGHT,

GL_COLOR_BUFFER_BIT, GL_NEAREST);

CheckForGLError();

}

void CleanUp()

{

// destroy all objects

glDeleteFramebuffers(1, &framebuffer);

glDeleteTextures(1, &framebuffertexture);

glDeleteProgram(program);

glDeleteShader(vertexshader);

glDeleteShader(fragmentshader);

glDeleteVertexArrays(1, &vertexarray);

glDeleteBuffers(1, &vertexbuffer);

}

void CheckForGLError()

{

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

{

cout << "OpenGL Error: \t";

if (error == GL_INVALID_ENUM)

cout << "GL_INVALID_ENUM";

if (error == GL_INVALID_VALUE)

cout << "GL_INVALID_VALUE";

if (error == GL_INVALID_OPERATION)

cout << "GL_INVALID_OPERATION";

if (error == GL_STACK_OVERFLOW)

cout << "GL_STACK_OVERFLOW";

if (error == GL_STACK_UNDERFLOW)

cout << "GL_STACK_UNDERFLOW";

if (error == GL_OUT_OF_MEMORY)

cout << "GL_OUT_OF_MEMORY";

if (error == GL_INVALID_FRAMEBUFFER_OPERATION)

cout << "GL_INVALID_FRAMEBUFFER_OPERATION";

if (error == GL_CONTEXT_LOST)

cout << "GL_CONTEXT_LOST";

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

cin.get();

}

}

void CheckFramebuffer(GLuint framebuffer, GLenum target)

{

GLenum status = glCheckNamedFramebufferStatus(framebuffer, target);

if (status != GL_FRAMEBUFFER_COMPLETE)

{

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

if (status == GL_FRAMEBUFFER_UNDEFINED)

cout << "undefined framebuffer";

if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)

cout << "a necessary attachment is uninitialized";

if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)

cout << "no attachments";

if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)

cout << "incomplete draw buffer";

if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)

cout << "incomplete read buffer";

if (status == GL_FRAMEBUFFER_UNSUPPORTED)

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

if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)

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

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

cin.get();

}

}

Shader.h

#pragma once

#include <string>

#include <list>

// shader functions

std::string LoadTextFile(const std::string& filepath);

std::string ShaderTypeName(unsigned int shader);

bool CompileShader(unsigned int shader, const std::string& sourcecode);

std::string ShaderInfoLog(unsigned int shader);

bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist);

std::string ProgramInfoLog(unsigned int program);

Shader.cpp

#include "Shader.h"

#include <iostream>

#include <fstream>

#include <GL/glew.h>

std::string LoadTextFile(const std::string & filepath)

{

std::string result(""), line;

std::fstream f(filepath, std::ios::in);

while (f.good())

{

std::getline(f, line);

result += line + '\n';

}

return result;

}

std::string ShaderTypeName(unsigned int shader)

{

if (glIsShader(shader))

{

int type;

glGetShaderiv(shader, GL_SHADER_TYPE, &type);

if (type == GL_VERTEX_SHADER)

return "Vertex Shader";

if (type == GL_TESS_CONTROL_SHADER)

return "Tessellation Control Shader";

if (type == GL_TESS_EVALUATION_SHADER)

return "Tessellation Evaluation Shader";

if (type == GL_GEOMETRY_SHADER)

return "Geometry Shader";

if (type == GL_FRAGMENT_SHADER)

return "Fragment Shader";

if (type == GL_COMPUTE_SHADER)

return "Compute Shader";

}

return "invalid shader";

}

bool CompileShader(unsigned int shader, const std::string& sourcecode)

{

if (!glIsShader(shader))

{

std::cout << "ERROR: shader compilation failed, no valid shader specified" << std::endl;

return false;

}

if (sourcecode.empty())

{

std::cout << "ERROR: shader compilation failed, no source code specified (" << ShaderTypeName(shader) << ")" << std::endl;

return false;

}

// array of source code components

const char* sourcearray[] = { sourcecode.c_str() };

// set source code

glShaderSource(shader, 1, sourcearray, NULL);

// compile shaders

glCompileShader(shader);

// check compile status

int status;

glGetShaderiv(shader, GL_COMPILE_STATUS, &status);

// successfully compiled shader

if (status == GL_TRUE)

return true;

// show compile errors

std::cout << "ERROR: shader compilation failed (" << ShaderTypeName(shader) << ")" << std::endl << ShaderInfoLog(shader) << std::endl;

return false;

}

std::string ShaderInfoLog(unsigned int shader)

{

if (glIsShader(shader))

{

int logsize;

char infolog[1024] = { 0 };

glGetShaderInfoLog(shader, 1024, &logsize, infolog);

return std::string(infolog);

}

return "invalid shader";

}

bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist)

{

if (!glIsProgram(program))

{

std::cout << "ERROR: shader linking failed, no valid program specified" << std::endl;

return false;

}

// attach all shaders to the program

for (auto& shader : shaderlist)

{

if (glIsShader(shader))

glAttachShader(program, shader);

}

// link program

glLinkProgram(program);

// detach all shaders again

for (auto& shader : shaderlist)

{

if (glIsShader(shader))

glDetachShader(program, shader);

}

int status;

glGetProgramiv(program, GL_LINK_STATUS, &status);

// successfully linked program

if (status == GL_TRUE)

return true;

// show link errors

std::cout << "ERROR: shader linking failed" << std::endl << ProgramInfoLog(program) << std::endl;

return false;

}

std::string ProgramInfoLog(unsigned int program)

{

if (glIsProgram(program))

{

int logsize;

char infolog[1024] = { 0 };

glGetProgramInfoLog(program, 1024, &logsize, infolog);

return std::string(infolog);

}

return "invalid program";

}