Preview:
// 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
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter