// the following are the resources used for this project :
// https://my.eng.utah.edu/~cs5610/handouts/order_independent_transparency.pdf
// https://learnopengl.com/Getting-started/Hello-Window
// https://learning.oreilly.com/library/view/opengl-build/9781788296724/ch06s02.html
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
void processInput(GLFWwindow *window);
void initialize_framebuffers();
// settings
const unsigned int SCR_WIDTH = 900;
const unsigned int SCR_HEIGHT = 800;
// framebuffers
GLuint FBO[2], FBOcolourBlender; // frame buffer objects
GLuint colourTextureID[2], depthTextureID[2], colourBlenderTextureID; // textures for attachment to frame buffer
// store vertex data in buffer
// VAO (vertex array object) stores pointers to one or more VBOs (vertex buffer object)
GLuint VAO, VBO, EBO; // EBO (element buffer object)
//total number of depth peeling passes
const int NUM_PASSES = 4;
// basic vertex shader source code
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 proj;\n"
"void main()\n"
"{\n"
" gl_Position = proj * view * model * vec4(aPos, 1.0);\n"
"}\0";
// basic fragment shader source code
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 cubecolour;\n" //colour uniform
"void main()\n"
"{\n"
" FragColor = cubecolour;\n"
"}\n\0";
// peeling vertex shader source code
const char* peeling_vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 proj;\n"
"void main()\n"
"{\n"
" gl_Position = proj * view * model * vec4(aPos, 1.0);\n"
"}\0";
// peeling fragment shader source code
// we compare the depth held in the depth texture of the FBO with the next depth
// if the next depth is less then or equal then we discard it
const char* peeling_fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 cubecolour;\n" //colour uniform
"uniform sampler2DRect depthTexture;\n" // depth
"void main()\n"
"{\n" // in order to extract the deoth from the sampler2DRect we need to flip (x , y, z, w) (s, t, p, q)
" float frontDepth = texture(depthTexture, gl_FragCoord.xy).s;\n" // gl_FragCoord returns the (x, y, z, 1/w) for the fragment
" if(gl_FragCoord.z <= frontDepth) discard;\n" // we want to see the back depth (equivalent to GL_GREATER)
" FragColor = cubecolour;\n"
"}\n\0";
GLfloat rotation_x = 0.0f;
GLfloat rotation_y = 0.0f;
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// vertice coordinates
GLfloat vertices[] = {
// set of vertices for each face
-0.25f, 0.25f, 0.25f,
-0.25f, -0.25f, 0.25f,
0.25f, 0.25f, 0.25f,
0.25f, -0.25f, 0.25f,
0.25f, 0.25f, 0.25f,
0.25f, -0.25f, 0.25f,
0.25f, 0.25f, -0.25f,
0.25f, -0.25f, -0.25f,
0.25f, 0.25f, -0.25f,
0.25f, -0.25f, -0.25f,
-0.25f, 0.25f, -0.25f,
-0.25f, -0.25f, -0.25f,
-0.25f, 0.25f, -0.25f,
-0.25f, -0.25f, -0.25f,
-0.25f, 0.25f, 0.25f,
-0.25f, -0.25f, 0.25f,
0.25f, -0.25f, -0.25f,
0.25f, -0.25f, 0.25f,
-0.25f, -0.25f, -0.25f,
-0.25f, -0.25f, 0.25f,
0.25f, 0.25f, 0.25f,
0.25f, 0.25f, -0.25f,
-0.25f, 0.25f, 0.25f,
-0.25f, 0.25f, -0.25f
};
// indices for vertices order
GLuint indices[] = {
0, 1, 2,
2, 1, 3,
4, 5, 6,
6, 5, 7,
8, 9, 10,
10, 9, 11,
12, 13, 14,
14, 13, 15,
16, 17, 18,
18, 17, 19,
20, 21, 22,
22, 21, 23
};
const int num_cubes = 6;
glm::vec3 cube_positions[6] = {
glm::vec3( 0.0f, 0.0f, 0.0f),
glm::vec3( 0.5f +0.2f, 0.0f, 0.0f),
glm::vec3( -0.5f -0.2f, 0.0f, 0.0f),
glm::vec3( 0.0f, 0.0f, 1.0f),
glm::vec3( 0.5f +0.2f, 0.0f, 1.0f),
glm::vec3( -0.5f -0.2f, 0.0f, 1.0f)
};
//constants for box colours
glm::vec4 box_colors[6]={
glm::vec4(1,0,0,1), // red
glm::vec4(0,1,0,1), // green
glm::vec4(0,0,1,1), // blue
glm::vec4(1,0,0,1), // red
glm::vec4(0,1,0,1), // green
glm::vec4(0,0,1,1) // blue
};
// glfw window of size SCR_WIDTH by SCR_HEIGHT
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Depth Peeling with OpenGL", NULL, NULL);
if (window == NULL) {
printf("failed to create GLFW window \n");
glfwTerminate();
return -1;
}
else { printf("GLFW window creation successful ! \n");}
glfwMakeContextCurrent(window); // tells glfw we want to use the window created
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
printf("failed to initialize GLAD \n");
return -1;
}
// specify the viewport window goes from x = 0 to x = SCR_WIDTH and y = 0 to y = SCR_HEIGHT
glViewport( 0, 0, SCR_WIDTH, SCR_HEIGHT);
//-----//
// basic shader
// vertex shader reference
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1 , &vertexShaderSource, NULL);
// compile shader into machine code
glCompileShader(vertexShader);
// fragment shader reference
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1 , &fragmentShaderSource, NULL);
// compile shader into machine code
glCompileShader(fragmentShader);
GLuint shaderProgram = glCreateProgram();
// attach shader to shader program
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// because the shaders are in the program we can delete
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// shders for peeling the front layer off
// vertex shader reference
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1 , &peeling_vertexShaderSource, NULL);
// compile shader into machine code
glCompileShader(vertexShader);
// fragment shader reference
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1 , &peeling_fragmentShaderSource, NULL);
// compile shader into machine code
glCompileShader(fragmentShader);
GLuint peeling_shaderProgram = glCreateProgram();
// attach shader to shader program
glAttachShader(peeling_shaderProgram, vertexShader);
glAttachShader(peeling_shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// because the shaders are in the program we can delete
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//-----//
initialize_framebuffers();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO); // because we only have one object
glGenBuffers(1, &EBO);
// binding object sets certain object to current object
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // draw mean vertices will be modified and used to draw images on the screen
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glEnable(GL_DEPTH_TEST);
//-----//
while (!glfwWindowShouldClose(window)) {
// check for keyboard input
processInput(window);
// set up for view and proj projections
// view is for the view from the camera
// proj is for clip space once the vertices are in the camera space by the view
// model is for the objects
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 proj = glm::mat4(1.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -4.0f)); // z is positive towards us and negative away from us
proj = glm::perspective(glm::radians(45.0f), (float)(SCR_WIDTH/SCR_HEIGHT), 0.1f, 100.0f); // 45.0 rad = fov
// anything 0.1f close to camera is clipped and 100f clipped
view = glm::rotate(view, glm::radians(rotation_x), glm::vec3(1.0f, 0.0f, 0.0f));
view = glm::rotate(view, glm::radians(rotation_y), glm::vec3(0.0f, 1.0f, 0.0f));
// clear buffers prior to looping
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// loop through the number of passes
for (int i = 0; i < NUM_PASSES; i++){
int A = i % 2;
int B = (i+1) % 2;
}
// point to matrix object itself
GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
GLint projLoc = glGetUniformLocation(shaderProgram, "proj");
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// opengl works by having two buffers, while one is being displayed the other is being made in the backround and they swap
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// clear back buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// our first dept peeling pass
glEnable(GL_DEPTH_TEST);
glUseProgram(shaderProgram);
// instancing of my cube
for (unsigned int i = 0; i < num_cubes; i++)
{
// calculate the model matrix for each object and pass it to shader before drawing
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cube_positions[i]); // translate the cubes to correct locations
GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniform4fv(glGetUniformLocation(shaderProgram, "cubecolour"),1 , &(box_colors[i].x)); // set the uniform cube colour in the fragment shader to our colour
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(int), GL_UNSIGNED_INT, 0);
}
/*
for (int i = 0; i < NUM_PASSES; i++){
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int A = i % 2;
int B = (i+1) % 2;
// depth buffer 1 to peel away previous fragment
glBindFramebuffer(GL_FRAMEBUFFER, FBO[A]);
//set the first colour attachment as draw buffer
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
// depth buffer 2 performs “regular” depth-buffering
// front - back using the fragment shader
glBindFramebuffer(GL_FRAMEBUFFER, FBO[B]);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
}*/
// swap back buffer with front buffer
glfwSwapBuffers(window); // image gets updated at each frame
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
const GLfloat rotation_speed = 5;
// exit
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
// rotate scene view
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_UP) == GLFW_REPEAT)
rotation_x -= rotation_speed;
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_REPEAT)
rotation_x += rotation_speed;
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_REPEAT)
rotation_y -= rotation_speed;
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_REPEAT)
rotation_y += rotation_speed;
}
void initialize_framebuffers() {
// set up two frame buffer objects, with a colour texture attachment and a depth textture attachment
glGenFramebuffers(2, FBO);
glGenTextures (2, colourTextureID);
glGenTextures (2, depthTextureID);
//for each attachment
for (int i = 0; i < 2; i++) {
// texture for depth
glBindTexture(GL_TEXTURE_RECTANGLE, depthTextureID[i]);
glTexImage2D(GL_TEXTURE_RECTANGLE , 0, GL_DEPTH_COMPONENT32F, SCR_WIDTH, SCR_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// texture for colour
glBindTexture(GL_TEXTURE_RECTANGLE, colourTextureID[i]);
glTexImage2D(GL_TEXTURE_RECTANGLE , 0,GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
// bind FBO and attach the depth and colour attachments
glBindFramebuffer(GL_FRAMEBUFFER, FBO[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, depthTextureID[i], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, colourTextureID[i], 0);
}
// texture for colour blending at the end
glGenTextures(1, &colourBlenderTextureID);
glBindTexture(GL_TEXTURE_RECTANGLE, colourBlenderTextureID);
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, 0);
//frame buffer for holding the colour blending texture and for depth (used at the end)
glGenFramebuffers(1, &FBOcolourBlender);
glBindFramebuffer(GL_FRAMEBUFFER, FBOcolourBlender);
// attach the textures
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, depthTextureID[0], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, colourBlenderTextureID, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status == GL_FRAMEBUFFER_COMPLETE )
printf("FBO setup successful ! \n");
else
printf("problem with FBO setup \n");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// depth peeling is almost like shadow mapping because with shadow mapping we have to get the first z inorder to see what casts the shadow