[OpenGL]使用OpenGL+OIT实现透明效果

一、简介

本文介绍了使用OpenGL,利用 Order-Independent Transparency, OIT技术中的加权混合(weighted blended)算法实现透明效果。

该算法在实现中使用四次渲染流程实现OIT透明效果,分别为:

  1. 使用 opaqueShader 渲染非透明模型。保存渲染得到的 color buffer 和 depth buffer,分别保存在 opaqueTextureopaqueDepthTexture 中;
  2. 使用 transparentShader 渲染透明模型。根据各个透明模型的颜色值C、透明度a和在view space中的深度z,计算各个模型对目标片段的颜色权重,将权重保存在 accumTexture 中,各片段对应的透明度加权和存在 revealTexture 中。该渲染流程与模型的顺序无关,即不需要事先对模型进行排序;
  3. 使用 compositeShader1.2.中得到的 opaqueTextureaccumTexturerevealTexture 混合起来,得到最终的渲染结果,保存到 opaqueTexture 中;
  4. 使用 screenShaderopaqueTexture 渲染到屏幕;

根据本文代码,如果顺利,最终你可以到的如下结果(渲染时不需要对透明模型进行排序处理,即,该算法是 Order-Independent Transparency (OIT)的):

二、基于加权混合的OIT

0. 基本思想

基于加权混合的 OIT(Weighted Blended Order-Independent Transparency)的基本思想是通过给每个透明片段分配一个权重,按照这些权重对颜色和透明度进行累积混合,从而避免对透明物体进行排序,同时实现近似的透明效果。每个透明片段的权重 w w w 根据片段的 透明度a 和片段在view space中的 深度z 计算得到,最后使用:
C = C 1 ∗ w ( a 1 , z 1 ) + C 2 ∗ w ( a 2 , z 2 ) + . . . + C n ∗ w ( a n , z n ) C = C_1*w(a_1,z_1) + C_2*w(a_2,z_2) + ... + C_n*w(a_n,z_n) C=C1∗w(a1,z1)+C2∗w(a2,z2)+...+Cn∗w(an,zn)

近似表示:
C = C 1 ∗ a 1 + ( 1 − a 1 ) ∗ ( C 2 ∗ a 2 + ( 1 − a 2 ) ∗ ( . . . ) ) C = C_1*a_1 + (1-a_1)*(C_2*a_2 + (1-a_2)*(...)) C=C1∗a1+(1−a1)∗(C2∗a2+(1−a2)∗(...))

这其中的关键是,如何确定各个片段的权重w(a,z)。通常权重w(a,z)都使用heuristic technique启发式方法确定,即人为地根据实际的效果调整权重方程。

这部分过于复杂本文不做过多介绍,读者可以参考LearnOpenGL-OIT-Weighted Blended 和 论文Weighted Blended Order-Independent Transparency

1. 渲染流程

基于加权混合的算法使用四次渲染流程实现OIT透明效果,分别为:

  1. 使用 opaqueShader 渲染非透明模型。保存渲染得到的 color buffer 和 depth buffer,分别保存在 opaqueTextureopaqueDepthTexture 中。opaqueTexture用做显示最终结果,opaqueDepthTexture用在接下来的2.中,用于丢弃掉被 非透明模型 遮挡的 透明模型。
  2. 使用 transparentShader 渲染透明模型。根据各个透明模型的颜色值C、透明度a和在view space中的深度z,计算各个模型对目标片段的颜色权重,将权重保存在 accumTexture 中,各片段对应的透明度加权和存在 revealTexture 中。该渲染流程与模型的顺序无关,即不需要事先对模型进行排序;
  3. 使用 compositeShader1.2.中得到的 opaqueTextureaccumTexturerevealTexture 混合起来,得到最终的渲染结果,保存到 opaqueTexture 中;
  4. 使用 screenShaderopaqueTexture 渲染到屏幕;

三、代码实现

0. 环境需要

  • Linux,或者 windos下使用wsl2。

  • 安装GLFW和GLAD。请参考[OpenGL] wsl2上安装使用cmake+OpenGL教程

  • 安装glmglm是个可以只使用头文件的库,因此可以直接下载release的压缩文件,然后解压到include目录下。例如,假设下载的release版本的压缩文件为glm-1.0.1-light.zip。将glm-1.0.1-light.zip复制include目录下,然后执行以下命令即可解压glm源代码:

    shell 复制代码
    unzip glm-1.0.1-light.zip
  • 需要使用Assimp库加载obj模型,在 ubuntu 下可以使用以下命令安装 Assimp

    shell 复制代码
    sudo apt-get update
    sudo apt-get install libassimp-dev
  • 需要下载 stb_image.h 作为加载.png图像的库。将 stb_image.h 下载后放入include/目录下。

1. main.cpp 介绍

1.1) 代码流程

  1. 初始化glfw,glad,窗口
  2. 编译 shader 程序
    2.1 opaque shader
    2.2 transparent shader
    2.3 composite shader
    2.4 screen shader
  3. 设置 FBO
    3.1 设置 opaqueFBO
    3.2 设置 transparentFBO
  4. 加载obj模型、纹理图片、透明物体模型
  5. 设置光源和相机位置,Blinn-Phong 模型参数
  6. 开始渲染
    6.1 渲染 opaque model -> opaqueShader -> opaqueTexture + depthTexture
    6.2 渲染 transparent models -> transparentShader -> accumTexture + revealTexture
    6.3 混合 screen model + accumTexture + revealTexture + opaqueTexture -> compositeShader-> opaqueTexture
    6.4 渲染 screen model + opaqueTexture -> screenShader -> default FBO (screen)
  7. 释放资源

1.2) main.cpp 代码

cpp 复制代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "Skybox.hpp"
#include "Shader.hpp"
#include "Mesh.hpp"
#include "Model.hpp"

#include "glm/ext.hpp"
#include "glm/mat4x4.hpp"
#include <cstdint>
#include <iostream>

#include <random>
#include <iostream>
// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
// 用于处理用户输入的函数
void processInput(GLFWwindow *window);

// 指定窗口默认width和height像素大小
unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;

/************************************/

int main()
{
    /****** 1.初始化glfw, glad, 窗口 *******/
    // glfw 初始化 + 配置 glfw 参数
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // 在创建窗口之前
    glfwWindowHint(GLFW_SAMPLES, 4); // 设置多重采样级别为4
    // glfw 生成窗口
    GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        // 检查是否成功生成窗口,如果没有成功打印出错信息并且退出
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // 设置窗口window的上下文
    glfwMakeContextCurrent(window);
    // 配置window变化时的回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // 使用 glad 加载 OpenGL 中的各种函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // 使用线框模式,绘制时只绘制 三角形 的轮廓
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // 使用填充模式,绘制时对 三角形 内部进行填充
    // 启用 多重采样抗锯齿
    glEnable(GL_MULTISAMPLE);
    /************************************/

    /****** 2.编译 shader 程序 ******/

    // 2.1 opaque shader. 使用Blinn-Phong模型渲染非透明(opaque)物体 shader, opaque results -> opaqueFBO
    Shader opaqueShader("../resources/Blinn-Phong.vert", "../resources/Blinn-Phong.frag");
    // 2.2 transparent shader. 渲染 透明物体的 shader,  transparent results -> transparentFBO
    Shader transparentShader("../resources/transparent.vert", "../resources/transparent.frag");
    // 2.3 composite shader. 将 transparentFBO 归一化的 shader, opaque results + transparent results -> opaqueFBO
    Shader compositeShader("../resources/composite.vert", "../resources/composite.frag");
    // 2.4 screen shader. 将 compositeFBO 当做纹理,显示在屏幕上的 shader, -> default FBO (screen)
    Shader screenShader("../resources/screen.vert", "../resources/screen.frag");
    /************************************/

    /****** 3. 设置FBO ******/
    unsigned int opaqueFBO, transparentFBO;
    glGenFramebuffers(1, &opaqueFBO);
    glGenFramebuffers(1, &transparentFBO);

    // 3.1 设置 opaqueFBO 对应的 texture
    // 设置 opaqueFBO 的渲染结果纹理, opaqueFBO 用于渲染 opaque 物体的 颜色->opaqueTexture, 深度值->opaqueDepthTexture
    unsigned int opaqueTexture;
    glGenTextures(1, &opaqueTexture);            // 生成 texture (opaqueTexture)
    glBindTexture(GL_TEXTURE_2D, opaqueTexture); // 绑定 texture (opaqueTexture 绑定到 CL_TEXTURE_2D 上)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_HALF_FLOAT,
                 NULL); // 设置 opaqueTexture 属性
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0); // 解绑 opaqueTexture

    unsigned int opaqueDepthTexture;                  // 生成 deepth texture (opaqueDepthTexture)
    glGenTextures(1, &opaqueDepthTexture);            // 绑定 texture (opaqueDepthTexture)
    glBindTexture(GL_TEXTURE_2D, opaqueDepthTexture); // 设置 opaqueDepthTexture 属性
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    // 将 opaqueTexture, opaqueDepthTexture 绑定到 opaqueGBO 上
    // opaqueTexture 用于接收 GL_COLOR_ATTACHMENT0
    // opaqueDepthTexture 用于接收 GL_DEPTH_ATTACHMENT
    glBindFramebuffer(GL_FRAMEBUFFER, opaqueFBO);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, opaqueTexture, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, opaqueDepthTexture, 0);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Opaque framebuffer is not complete!" << std::endl;

    glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑  opaqueFBO

    // 3.2 设置 transparentFBO 对应的 texture
    // 设置 transparentFBO 的渲染纹理
    // transparentShader 中的 accum -> accumTexture, reveal -> revealTexture
    unsigned int accumTexture;
    glGenTextures(1, &accumTexture);
    glBindTexture(GL_TEXTURE_2D, accumTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_HALF_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    unsigned int revealTexture;
    glGenTextures(1, &revealTexture);
    glBindTexture(GL_TEXTURE_2D, revealTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, transparentFBO);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, accumTexture, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, revealTexture, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, opaqueDepthTexture,
                           0); // opaque framebuffer's depth texture

    const GLenum transparentDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
    glDrawBuffers(2, transparentDrawBuffers);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Transparent framebuffer is not complete!" << std::endl;

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    /****** 4.加载obj模型、纹理图片、透明物体模型 ******/
    // 4.1 opaque model
    Model ourModel("../resources/models/spot/spot.obj");
    // Model ourModel("../resources/models/nanosuit/nanosuit.obj");

    // 4.2 transparent model 0
    vector<Vertex> transWinVertex0 = {
        {{-0.80, 0.20, -1.0}, {0.0, 0.0, 1.0}, {0.0, 1.0}},  // position, normal, texture_coordinate
        {{-0.80, -0.80, -1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0}}, //
        {{0.20, -0.80, -1.0}, {0.0, 0.0, 1.0}, {1.0, 0.0}},  //
        {{0.20, 0.20, -1.0}, {0.0, 0.0, 1.0}, {1.0, 1.0}}};
    vector<unsigned int> transWinIndex0 = {0, 1, 2, 2, 3, 0};
    vector<Texture> transWinTexture0 = {
        {TextureFromFile("window-r.png", "../resources/textures", true, GL_CLAMP_TO_EDGE), "texture_diffuse",
         "../resources/textures/window-r.png"}}; // textire_id, type, file_path
    Mesh transWinMesh0(transWinVertex0, transWinIndex0, transWinTexture0);
    Model transWinModel0(transWinMesh0);

    // 4.3 transparent model 1
    vector<Vertex> transWinVertex1 = {
        {{-0.10, 0.90, -1.2}, {0.0, 0.0, 1.0}, {0.0, 1.0}},  //  position, normal, texture_coordinate
        {{-0.10, -0.10, -1.2}, {0.0, 0.0, 1.0}, {0.0, 0.0}}, //
        {{0.90, -0.10, -1.2}, {0.0, 0.0, 1.0}, {1.0, 0.0}},  //
        {{0.90, 0.90, -1.2}, {0.0, 0.0, 1.0}, {1.0, 1.0}}};
    vector<unsigned int> transWinIndex1 = {0, 1, 2, 2, 3, 0};
    vector<Texture> transWinTexture1 = {
        {TextureFromFile("window-g.png", "../resources/textures", true, GL_CLAMP_TO_EDGE), "texture_diffuse",
         "../resources/textures/window-g.png"}}; // textire_id, type, file_path
    Mesh transWinMesh1(transWinVertex1, transWinIndex1, transWinTexture1);
    Model transWinModel1(transWinMesh1);

    // 4.4 transparent model 2
    vector<Vertex> transWinVertex2 = {
        {{-0.10, 0.2, 1.2}, {0.0, 0.0, 1.0}, {0.0, 1.0}},  //  position, normal, texture_coordinate
        {{-0.10, -0.8, 1.2}, {0.0, 0.0, 1.0}, {0.0, 0.0}}, //
        {{0.90, -0.8, 1.2}, {0.0, 0.0, 1.0}, {1.0, 0.0}},  //
        {{0.90, 0.2, 1.2}, {0.0, 0.0, 1.0}, {1.0, 1.0}}};
    vector<unsigned int> transWinIndex2 = {0, 1, 2, 2, 3, 0};
    vector<Texture> transWinTexture2 = {
        {TextureFromFile("window-b.png", "../resources/textures", true, GL_CLAMP_TO_EDGE), "texture_diffuse",
         "../resources/textures/window-b.png"}}; // textire_id, type, file_path
    Mesh transWinMesh2(transWinVertex2, transWinIndex2, transWinTexture2);
    Model transWinModel2(transWinMesh2);

    // 4.5 screen model
    vector<Vertex> screenVertices = {{{-1, 1, 0}, {0, 1, 0}, {0, 1.0}},
                                     {{-1, -1, 0}, {0, 1, 0}, {0, 0}},
                                     {{1, -1, 0}, {0, 1, 0}, {1.0, 0}},
                                     {{1, 1, 0}, {0, 1, 0}, {1.0, 1.0}}};

    vector<unsigned int> screenIndices = {0, 1, 2, 0, 2, 3};
    vector<Texture> screenTexture; // 空纹理, 在渲染时会将 opaqueTexture 设为 screenModel 的纹理
    Mesh screenMesh(screenVertices, screenIndices, screenTexture);
    Model screenModel(screenMesh);

    /************************************/

    /****** 5.设置光源和相机位置,Blinn-Phong 模型参数 ******/
    // 模型参数 ka, kd, ks
    float k[] = {0.1f, 0.7f, 0.2f}; // ka, kd, ks
    // 光源位置
    glm::vec3 light_pos = glm::vec3(-2.0f, 2.0f, 0.0f);
    // 相机位置
    glm::vec3 camera_pos = glm::vec3(0.0f, 0.0f, 1.5f);
    /************************************/

    /****** 6.开始渲染 ******/
    // 6.1 渲染 opaque model -> opaqueShader -> opaqueTexture + depthTexture
    float rotate = 180.0f;
    // 初始值 0.0, 1.0
    glm::vec4 zeroFillerVec(0.0f);
    glm::vec4 oneFillerVec(1.0f);
    while (!glfwWindowShouldClose(window))
    {
        rotate += 0.05f;
        // input
        // -----
        processInput(window);

        // render
        // ------

        // 6.1 渲染 opaque model -> opaqueShader -> opaqueTexture + depthTexture
        // 6.1.1). set {FBO (must be set first), depth test, blend, color/depth buffer (optional, must after set FBO)}
        glBindFramebuffer(GL_FRAMEBUFFER, opaqueFBO); // 渲染到 opaqueFBO (opaqueTexture, opaqueDepthTexture)
        glEnable(GL_DEPTH_TEST);                      // 启用 depth test
        glDepthFunc(GL_LESS);                         // depth test 通过 条件为 less
        glDepthMask(GL_TRUE);                         // 可以写入 depth buffer
        glDisable(GL_BLEND);                          // 禁止 blend

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT |
                GL_DEPTH_BUFFER_BIT); // 使用 (0.2,0.3,0.3,1.0)清空 color texture, 清空 depth buffer

        // 6.1.2). use shader
        opaqueShader.use(); // 启用 opaqueShader

        // 6.1.3). set MVP matrixes
        // 设置 camera_MVP 矩阵, 假设以 camera 为视角,渲染 camera 视角下的场景深度图
        // camera model 矩阵
        glm::mat4 camera_model = glm::mat4(1.0f);
        camera_model = glm::translate(camera_model, glm::vec3(0.0f, 0.0f, 0.0f));
        camera_model = glm::rotate(camera_model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f));
        camera_model = glm::rotate(camera_model, glm::radians(rotate), glm::vec3(0.0f, 1.0f, 0.0f));
        camera_model = glm::rotate(camera_model, glm::radians(0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
        camera_model = glm::scale(camera_model, glm::vec3(0.5f, 0.5f, 0.5f));
        // camera view 矩阵
        glm::mat4 camera_view = glm::mat4(1.0f);
        camera_view = glm::lookAt(camera_pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        // camera projection 矩阵
        glm::mat4 camera_projection = glm::mat4(1.0f);
        camera_projection = glm::perspective(glm::radians(60.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        opaqueShader.setMat4("model", camera_model);
        opaqueShader.setMat4("view", camera_view);
        opaqueShader.setMat4("projection", camera_projection);
        opaqueShader.setVec3("k", k[0], k[1], k[2]);
        opaqueShader.setVec3("cameraPos", camera_pos);
        opaqueShader.setVec3("lightPos", light_pos);

        // 6.1.4). Draw()
        ourModel.Draw(opaqueShader);

        // 6.1.5). set default FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        // 6.2 渲染 transparent models -> transparentShader -> accumTexture + revealTexture

        // 6.2.1). set {FBO (must be set first), depth test, blend, color/depth buffer (must after set FBO)}
        glBindFramebuffer(GL_FRAMEBUFFER, transparentFBO); // 渲染到 transparentFBO (accumTexture, revealTexture)
        glDepthMask(GL_FALSE);                             // 禁止写入 depth buffer
        glEnable(GL_BLEND);                                // 启用 blend
        glBlendFunci(0, GL_ONE, GL_ONE);                   // 设置 color_attachment0 的混合权重为 1,1
        // C_d_accum = C_s + C_d_accum
        glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); // 设置 color_attachment1 的混合权重为 0, 1-C_s
        // C_d_reveal = C_d_reveal * (1-C_s_color)
        glBlendEquation(GL_FUNC_ADD); // 设置 blend 运算为 ADD 运算, 实际上不设置也可以,因为默认就是 ADD 运算
        glClearBufferfv(GL_COLOR, 0, &zeroFillerVec[0]); // 初始化 color_attachment0 (accumTexture) 值为 0.0
        glClearBufferfv(GL_COLOR, 1, &oneFillerVec[0]);  // 初始化 color_attachment1 (revealTexture) 值为1.0

        // 6.2.2). use shader
        transparentShader.use();

        // 6.2.3). set MVP matrixes
        transparentShader.setMat4("model", camera_model);
        transparentShader.setMat4("view", camera_view);
        transparentShader.setMat4("projection", camera_projection);

        // 6.2.4). Draw()
        transWinModel0.Draw(transparentShader);
        transWinModel1.Draw(transparentShader);
        transWinModel2.Draw(transparentShader);

        // 6.2.5). unbind transparentFBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        // 6.3 混合 screen model + accumTexture + revealTexture + opaqueTexture -> compositeShader-> opaqueTexture
        // 6.3.1). set {FBO (must be set first), depth test, blend, color/depth buffer (optional, must after set FBO)}
        glBindFramebuffer(GL_FRAMEBUFFER, opaqueFBO);      // 绑定 opaqueFBO
        glDepthFunc(GL_ALWAYS);                            // 总是通过 depth test
        glEnable(GL_BLEND);                                // 启用 blend
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 混合权重设为 alpha_src, 1-alpha_src

        // 6.3.2) use shader
        compositeShader.use();

        // 6.3.4) set texture
        screenModel.setTexture(
            {{accumTexture, "texture_diffuse", "accumTexture"}, {revealTexture, "texture_diffuse", "revealTexture"}});
        // 6.3.5) Draw()
        screenMesh.DrawWithTextures(
            compositeShader,
            {{accumTexture, "texture_diffuse", "accumTexture"}, {revealTexture, "texture_diffuse", "revealTexture"}},
            {"accum", "reveal"});
        // 6.3.6) 解绑 opaqueFBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        // 6.4 渲染 screen model + opaqueTexture -> screenShader -> default FBO (screen)
        // 6.4.1). set {FBO (must be set first), depth test, blend, color/depth buffer (must after set FBO)}
        glBindFramebuffer(GL_FRAMEBUFFER, 0); // 渲染到屏幕
        glDisable(GL_DEPTH_TEST);             // 关闭深度测试
        glDepthMask(GL_TRUE);                 // 开启写入 depth buffer, 使用 glClear() 清空 depth buffer
        glDisable(GL_BLEND);                  // 关闭 blend
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // 清空 clor, depth, stencil 缓冲
        // 6.4.2) use shader
        screenShader.use();
        // 6.4.3) set texture
        screenModel.setTexture({{opaqueTexture, "texture_diffuse", "opaqueTexture"}});
        // 6.4.4) Draw
        screenModel.Draw(screenShader);

        glfwSwapBuffers(window); // 在gfw中启用双缓冲,确保绘制的平滑和无缝切换
        glfwPollEvents(); // 用于处理所有挂起的事件,例如键盘输入、鼠标移动、窗口大小变化等事件
    }

    /************************************/
    /****** 7.释放资源 ******/
    // glfw 释放 glfw使用的所有资源
    glfwTerminate();
    /************************************/
    return 0;
}

// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{
    // 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, true);
    }
}

// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    SCR_WIDTH = width;
    SCR_HEIGHT = height;
    glViewport(0, 0, width, height);
}

由于篇幅问题,本文不再介绍其他文件中的代码细节,全部代码和相关模型可以在三、全部代码中下载。

2. 编译运行及结果

编译运行:

cpp 复制代码
cd ./build
cmake ..
make
./OpenGL_OIT

渲染结果:

四、全部代码

全部代码以及模型文件可以在OpenGL使用OpenGL+OIT实现透明效果中下载。

五、参考

[1.] LearnOpenGL-OIT-Weighted Blended

[2.] Weighted Blended Order-Independent Transparency

相关推荐
三翼鸟数字化技术团队21 小时前
模型工作流:自动化的模型内部三角面剔除
计算机图形学·图形学
刘好念2 天前
[OpenGL]使用 Compute Shader 实现矩阵点乘
c++·计算机图形学·opengl·glsl
阳光开朗_大男孩儿3 天前
为什么glfwWindowHint设置的属性,glfwCreateWindow可以直接使用?
前端·数据库·opengl
charlee445 天前
深度科普文:细数倾斜摄影数据的缺点
三维可视化·计算机图形学·倾斜摄影
刘好念5 天前
[OpenGL]使用TransformFeedback实现粒子效果
c++·计算机图形学·opengl
吃豆腐长肉7 天前
着色器 (三)
opengl·着色器
吃豆腐长肉7 天前
opengl 着色器 (四)最终章收尾
opengl·着色器
德林恩宝8 天前
WebGPU、WebGL 和 OpenGL/Vulkan对比分析
web·webgl·opengl·webgpu
zaizai100712 天前
LearnOpenGL学习(高级OpenGL -> 高级GLSL,几何着色器,实例化)
opengl