// 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