Full Source Code of main.cpp
#include <iostream>
#include <windows.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "shader.h"
#include "Mesh.h"
#include "Node.h"
static Shader shader;
glm::mat4 matModelRoot = glm::mat4(1.0);
glm::mat4 matView = glm::mat4(1.0);
glm::mat4 matProj = glm::ortho(-2.0f,2.0f,-2.0f,2.0f, -2.0f,2.0f);
glm::vec3 lightPos = glm::vec3(5.0f, 5.0f, 10.0f);
glm::vec3 viewPos = glm::vec3(0.0f, 0.0f, 5.0f);
GLuint blinnShader;
GLuint texblinnShader;
//==================================================
// for LabA10 bloom effects
GLuint bloomrenderShader;
GLuint bloomfilterShader;
GLuint bloomblurShader;
GLuint bloomblendShader;
// framebuffer ID
GLuint colourFBO, blurFBO;
// render texture ID and blurring buffer ID
GLuint colourTex, blurtex[2];
int width = 800;
int height = 800;
int bloomBufWidth;
int bloomBufHeight;
std::shared_ptr<Node> scene;
std::shared_ptr<Mesh> square;
std::shared_ptr<Mesh> teapot;
void initRenderToTexture()
{
// Generate and bind the framebuffer
glGenFramebuffers(1, &colourFBO);
glBindFramebuffer(GL_FRAMEBUFFER, colourFBO);
// Create the texture object
glGenTextures(1, &colourTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, colourTex);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB32F, width, height);
// Bind the texture to the colour FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colourTex, 0);
// Set the targets for the fragment output variables
GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, drawBuffers);
// Create the depth buffer
GLuint depthBuf;
glGenRenderbuffers(1, &depthBuf);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
// Bind the depth buffer to the FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depthBuf);
// Create an FBO for the bright-pass filter and blur
glGenFramebuffers(1, &blurFBO);
glBindFramebuffer(GL_FRAMEBUFFER, blurFBO);
// the bloom buffer should use a lower resolution to better support blurring
// such as width/4 and height/4 or more
bloomBufWidth = width / 2;
bloomBufHeight = height / 2;
// Create two texture objects to ping-pong blur
glGenTextures(2, blurtex);
glActiveTexture(GL_TEXTURE2);
// blur texture 1
glBindTexture(GL_TEXTURE_2D, blurtex[0]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB32F, bloomBufWidth, bloomBufHeight);
//glActiveTexture(GL_TEXTURE2);
// blur texture 2
glBindTexture(GL_TEXTURE_2D, blurtex[1]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB32F, bloomBufWidth, bloomBufHeight);
// Bind texture 1 to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurtex[0], 0);
// Unbind the framebuffer, and revert to default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void render()
{
glViewport(0, 0, width, height);
// comment glBindFramebuffer to test rendering
glBindFramebuffer(GL_FRAMEBUFFER, colourFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
scene->setShaderId(bloomrenderShader);
scene->draw(matModelRoot, matView, matProj);
//teapot->setShaderId(bloomrenderShader);
//teapot->draw(matModelRoot, matView, matProj);
}
void filter()
{
// unbind the framebuffer to test it in your window
//glBindFramebuffer(GL_FRAMEBUFFER,0);
glBindFramebuffer(GL_FRAMEBUFFER, blurFBO);
// set the viewport size to bloom buffer width and height
glViewport(0, 0, bloomBufWidth, bloomBufHeight);
// depth test is not needed for image processing
glDisable(GL_DEPTH_TEST);
// clear the background
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// draw the full screen square
square->setShaderId(bloomfilterShader);
square->draw(glm::mat4(1.0f), glm::mat4(1.0f), glm::mat4(1.0f));
}
void blur(int pass)
{
GLuint loc = glGetUniformLocation(bloomblurShader, "pass" );
glUniform1i(loc, pass);
glActiveTexture(GL_TEXTURE2);
if ((pass % 2) == 0)
{
// rending from texture 1 and writing to texture 2
glBindTexture(GL_TEXTURE_2D, blurtex[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurtex[1], 0);
}
else
{
// reading from texture 2 and writing to texture 1
glBindTexture(GL_TEXTURE_2D, blurtex[1]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurtex[0], 0);
}
// Render the full-screen quad
square->setShaderId(bloomblurShader);
square->draw(glm::mat4(1.0f), glm::mat4(1.0f), glm::mat4(1.0f));
}
void blend()
{
// Bind to the default framebuffer to draw on the screen
glBindFramebuffer(GL_FRAMEBUFFER,0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, width, height);
// Render the full-screen quad
square->setShaderId(bloomblendShader);
square->draw(glm::mat4(1.0f), glm::mat4(1.0f), glm::mat4(1.0f));
}
// Initialize shader
GLuint initShader(std::string pathVert, std::string pathFrag)
{
shader.read_source( pathVert.c_str(), pathFrag.c_str());
shader.compile();
glUseProgram(shader.program);
return shader.program;
}
void setLightPosition(glm::vec3 lightPos)
{
GLuint lightpos_loc = glGetUniformLocation(shader.program, "lightPos" );
glUniform3fv(lightpos_loc, 1, glm::value_ptr(lightPos));
}
void setViewPosition(glm::vec3 eyePos)
{
GLuint viewpos_loc = glGetUniformLocation(shader.program, "viewPos" );
glUniform3fv(viewpos_loc, 1, glm::value_ptr(eyePos));
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
glm::mat4 mat = glm::mat4(1.0);
float angleStep = 5.0f;
float transStep = 1.0f;
if (action == GLFW_PRESS) {
if (mods & GLFW_MOD_CONTROL) {
// translation in world space
if (GLFW_KEY_LEFT == key) {
mat = glm::translate(glm::mat4(1.0f), glm::vec3(transStep, 0.0f, 0.0f));
matModelRoot = mat * matModelRoot;
} else if (GLFW_KEY_RIGHT == key ) {
mat = glm::translate(glm::mat4(1.0f), glm::vec3(-transStep, 0.0f, 0.0f));
matModelRoot = mat * matModelRoot;
} else if (GLFW_KEY_UP == key) {
mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, transStep, 0.0f));
matModelRoot = mat * matModelRoot;
} if (GLFW_KEY_DOWN == key) {
mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -transStep, 0.0f));
matModelRoot = mat * matModelRoot;
}
}
// camera control
if (GLFW_KEY_LEFT == key) {
// pan left, rotate around Y, CCW
std::cout << "left arrow" << std::endl;
mat = glm::rotate(glm::radians(-angleStep), glm::vec3(0.0, 1.0, 0.0));
matView = mat * matView;
} else if (GLFW_KEY_RIGHT == key ) {
// pan right, rotate around Y, CW
mat = glm::rotate(glm::radians(angleStep), glm::vec3(0.0, 1.0, 0.0));
matView = mat * matView;
} else if (GLFW_KEY_UP == key) {
// tilt up, rotate around X, CCW
mat = glm::rotate(glm::radians(-angleStep), glm::vec3(1.0, 0.0, 0.0));
matView = mat * matView;
} if (GLFW_KEY_DOWN == key) {
// tilt down, rotate around X, CW
mat = glm::rotate(glm::radians(angleStep), glm::vec3(1.0, 0.0, 0.0));
matView = mat * matView;
} else if ( (GLFW_KEY_KP_ADD == key) ||
(GLFW_KEY_EQUAL == key) && (mods & GLFW_MOD_SHIFT) ) {
// std::cout << "+ pressed" << std::endl;
// zoom in, move along -Z
mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, transStep));
matView = mat * matView ;
} else if ( (GLFW_KEY_KP_SUBTRACT == key ) || (GLFW_KEY_MINUS == key) ) {
// std::cout << "keypad - pressed" << std::endl;
// zoom out, move along -Z
mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -transStep));
matView = mat * matView;
} else if (GLFW_KEY_R == key) {
//std::cout << "R pressed" << std::endl;
// reset
matView = glm::lookAt(glm::vec3(0, 0, 5), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
matModelRoot = glm::mat4(1.0f);
}
// translation along camera axis (first person view)
else if (GLFW_KEY_A == key ) {
//if (modes & GLFW_MOD_CONTROL)
// move left along -X
mat = glm::translate(glm::mat4(1.0f), glm::vec3(transStep, 0.0f, 0.0f));
matView = mat * matView;
} else if (GLFW_KEY_D == key) {
// move right along X
mat = glm::translate(glm::mat4(1.0f), glm::vec3(-transStep, 0.0f, 0.0f));
matView = mat * matView;
} if (GLFW_KEY_W == key ) {
// move forward along -Z
mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, transStep));
matView = mat * matView;
} else if (GLFW_KEY_S == key) {
// move backward along Z
mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -transStep));
matView = mat * matView;
}
}
}
int main()
{
GLFWwindow *window;
// GLFW init
if (!glfwInit())
{
std::cout << "glfw failed" << std::endl;
return -1;
}
// create a GLFW window
window = glfwCreateWindow(width, height, "Hello OpenGL 10 Bloom", NULL, NULL);
glfwMakeContextCurrent(window);
// register the key event callback function
glfwSetKeyCallback(window, key_callback);
// loading glad
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Couldn't load opengl" << std::endl;
glfwTerminate();
return -1;
}
blinnShader = initShader( "shaders/blinn.vert", "shaders/blinn.frag");
setLightPosition(lightPos);
setViewPosition(viewPos);
texblinnShader = initShader("shaders/texblinn.vert", "shaders/texblinn.frag");
setLightPosition(lightPos);
setViewPosition(viewPos);
//==============================================================================
// added for LabA10 Bloom
bloomrenderShader = initShader("shaders/bloom.vert", "shaders/bloomrender.frag");
setLightPosition(lightPos);
setViewPosition(viewPos);
bloomfilterShader = initShader("shaders/bloom.vert", "shaders/bloomfilter.frag");
bloomblurShader = initShader("shaders/bloom.vert", "shaders/bloomblur.frag");
bloomblendShader = initShader("shaders/bloom.vert", "shaders/bloomblend.frag");
//==============================================================================
// set the eye at (0, 0, 5), looking at the centre of the world
// try to change the eye position
viewPos = glm::vec3(0.0f, 2.0f, 5.0f);
matView = glm::lookAt(viewPos, glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
// set the Y field of view angle to 60 degrees, width/height ratio to 1.0, and a near plane of 3.5, far plane of 6.5
// try to play with the FoV
matProj = glm::perspective(glm::radians(60.0f), 1.0f, 2.0f, 8.0f);
//----------------------------------------------------
// Meshes
std::shared_ptr<Mesh> cube = std::make_shared<Mesh>();
cube->init("models/cube.obj", blinnShader);
teapot = std::make_shared<Mesh>();
teapot->init("models/teapot.obj", blinnShader);
std::shared_ptr<Mesh> bunny = std::make_shared<Mesh>();
bunny->init("models/bunny_normal.obj", texblinnShader);
square = Mesh::createSquare();
//----------------------------------------------------
// Nodes
scene = std::make_shared<Node>();
std::shared_ptr<Node> teapotNode = std::make_shared<Node>();
std::shared_ptr<Node> cubeNode = std::make_shared<Node>();
std::shared_ptr<Node> bunnyNode = std::make_shared<Node>();
//----------------------------------------------------
// Build the tree
teapotNode->addMesh(teapot);
cubeNode->addMesh(cube, glm::mat4(1.0), glm::mat4(1.0), glm::scale(glm::vec3(2.0f, 0.25f, 1.5f)));
bunnyNode->addMesh(bunny, glm::mat4(1.0), glm::mat4(1.0), glm::scale(glm::vec3(0.005f, 0.005f, 0.005f)));
cubeNode->addChild(teapotNode, glm::translate(glm::vec3(-1.5f, 0.5f, 0.0f)));
cubeNode->addChild(bunnyNode, glm::translate(glm::vec3(1.0f, 1.5f, 0.0f)));
// cubeNode->addChild(teapotNode, glm::translate(glm::vec3(0.0f, 1.0f, 0.0f)), glm::rotate(glm::radians(45.0f), glm::vec3(0.0f, 1.0f, 0.0f)));
//----------------------------------------------------
// Add the tree to the world space
scene->addChild(cubeNode);
//scene->addChild(bunnyNode);
// scene->addChild(cubeNode, glm::translate(glm::vec3(1.0f, 0.0f, 0.0f)), glm::rotate(glm::radians(45.0f), glm::vec3(1.0f, 0.0f, 0.0f)));
initRenderToTexture();
// setting the background colour, you can change the value
glClearColor(0.25f, 0.5f, 0.75f, 1.0f);
glEnable(GL_DEPTH_TEST);
//glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
// setting the event loop
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
// Step 1
render();
// Step 2
filter();
// Step 3
for (int i = 0; i < 50; i++)
{
blur(i);
}
// Step 4
blend();
//scene->draw(matModelRoot, matView, matProj);
//bunny->draw(glm::scale(glm::vec3(0.005f, 0.005f, 0.005f)), matView, matProj);
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}Last updated