一、简介
本文介绍了使用OpenGL,利用 Order-Independent Transparency, OIT技术中的加权混合(weighted blended)
算法实现透明效果。
该算法在实现中使用四次渲染流程实现OIT透明效果,分别为:
- 使用
opaqueShader
渲染非透明模型。保存渲染得到的 color buffer 和 depth buffer,分别保存在opaqueTexture
和opaqueDepthTexture
中; - 使用
transparentShader
渲染透明模型。根据各个透明模型的颜色值C、透明度a和在view space中的深度z,计算各个模型对目标片段的颜色权重,将权重保存在accumTexture
中,各片段对应的透明度加权和存在revealTexture
中。该渲染流程与模型的顺序无关,即不需要事先对模型进行排序; - 使用
compositeShader
将1.
和2.
中得到的opaqueTexture
、accumTexture
和revealTexture
混合起来,得到最终的渲染结果,保存到opaqueTexture
中; - 使用
screenShader
将opaqueTexture
渲染到屏幕;
根据本文代码,如果顺利,最终你可以到的如下结果(渲染时不需要对透明模型进行排序处理,即,该算法是 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透明效果,分别为:
- 使用
opaqueShader
渲染非透明模型。保存渲染得到的 color buffer 和 depth buffer,分别保存在opaqueTexture
和opaqueDepthTexture
中。opaqueTexture
用做显示最终结果,opaqueDepthTexture
用在接下来的2.
中,用于丢弃掉被 非透明模型 遮挡的 透明模型。 - 使用
transparentShader
渲染透明模型。根据各个透明模型的颜色值C、透明度a和在view space中的深度z,计算各个模型对目标片段的颜色权重,将权重保存在accumTexture
中,各片段对应的透明度加权和存在revealTexture
中。该渲染流程与模型的顺序无关,即不需要事先对模型进行排序; - 使用
compositeShader
将1.
和2.
中得到的opaqueTexture
、accumTexture
和revealTexture
混合起来,得到最终的渲染结果,保存到opaqueTexture
中; - 使用
screenShader
将opaqueTexture
渲染到屏幕;
三、代码实现
0. 环境需要
-
Linux,或者 windos下使用wsl2。
-
安装GLFW和GLAD。请参考[OpenGL] wsl2上安装使用cmake+OpenGL教程。
-
安装glm。glm是个可以只使用头文件的库,因此可以直接下载release的压缩文件,然后解压到
include
目录下。例如,假设下载的release版本的压缩文件为glm-1.0.1-light.zip
。将glm-1.0.1-light.zip
复制include
目录下,然后执行以下命令即可解压glm源代码:shellunzip glm-1.0.1-light.zip
-
需要使用
Assimp
库加载obj模型,在 ubuntu 下可以使用以下命令安装Assimp
:shellsudo apt-get update sudo apt-get install libassimp-dev
-
需要下载 stb_image.h 作为加载
.png
图像的库。将 stb_image.h 下载后放入include/
目录下。
1. main.cpp 介绍
1.1) 代码流程
- 初始化glfw,glad,窗口
- 编译 shader 程序
2.1 opaque shader
2.2 transparent shader
2.3 composite shader
2.4 screen shader - 设置 FBO
3.1 设置 opaqueFBO
3.2 设置 transparentFBO - 加载obj模型、纹理图片、透明物体模型
- 设置光源和相机位置,Blinn-Phong 模型参数
- 开始渲染
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) - 释放资源
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实现透明效果中下载。