当前位置: 首页 > news >正文

LearnOpenGL——延迟渲染学习笔记

延迟渲染学习笔记

  • 一、基本概念
  • 二、G-Buffer
    • MRT
  • 三、Lighting Pass
  • 四、结合延迟渲染和前向渲染
  • 五、更多光源

我们之前使用的一直是 前向渲染(正向渲染 Forward Rendering),指的是在场景中根据所有光源照亮一个物体,之后再渲染下一个物体。对程序性能影响很大,因为对于每一个需要渲染的物体,程序都要对每一个光源每一个需要渲染的片段进行迭代。 而且还有一部分被渲染的片段会被覆盖遮挡,造成浪费

一、基本概念

延迟渲染(Deferred Rendering) ,包含两个Pass:

  • 第一个是几何处理Pass:先渲染场景一次,之后获取对象的各种几何信息(比如顶点位置、颜色、法线、高光信息等),并存储在G-Buffer中
    在这里插入图片描述
  • 然后第二个Pass是用于计算光照信息——Lighting Pass:我们将会渲染一个满屏的方片并根据G-Buffer中的几何数据信息来为每个片元进行光照计算(在G-Buffer中每个像素进行迭代)。
    在这里插入图片描述

与前向渲染不同,延迟渲染并不是将每个对象进行顶点着色器到片元着色器的计算,而是将片元着色器移动到后期处理。这样保证了对于在光照处理阶段中处理的每一个像素都只处理一次,所以我们能够省下很多无用的渲染调用。

缺陷:

  • 显存消耗会较大:G-Buffer需要我们存储较大的几何数据
  • 不能使用MSAA:因为我们通过只有几何数据的G-Buffer来进行着色

二、G-Buffer

G-Buffer是一个用来存储光照计算所需数据的纹理的总称。

  • 3D位置向量,来计算片段位置变量 lightDir,viewDir
  • 3D法向量,normal
  • RGB漫反射颜色向量,Albedo
  • 镜面强度(高光反射强度)
  • 光源的位置向量、颜色向量
  • 观察者的位置向量

在前向渲染中,我们每个物体的光照计算都是根据特定实时数据的,所以如何给Lighting Pass传递正确的光照数据是很重要的。延迟渲染G-Buffer已经把几何数据渲染到一张2D纹理中,纹理允许我们存储各种各样的数据类型,所以纹理上每个片元都有正确的几何数据供光照计算

while(...) // render loop
{// 1. geometry pass: render all geometric/color data to g-buffer glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);glClearColor(0.0, 0.0, 0.0, 1.0); // keep it black so it doesn't leak into g-bufferglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);gBufferShader.use();for(Object obj : Objects){ConfigureShaderTransformsAndUniforms();obj.Draw();}  // 2. lighting pass: use g-buffer to calculate the scene's lightingglBindFramebuffer(GL_FRAMEBUFFER, 0);lightingPassShader.use();BindAllGBufferTextures();SetLightingUniforms();RenderQuad();
}

MRT

在几何处理阶段,我们需要渲染场景中所有物体,并且存储这些几何数据在G-Buffer中。我们可以使用 MRT 来在一个Pass中渲染多个颜色缓冲。

我们需要初始化一个帧缓冲gBuffer(这个gBuffer会有多个颜色缓冲附件,以及一个深度渲染缓冲对象)。对于位置和法向量的纹理,我们希望使用高精度的纹理(每分量16或32位的浮点数),而对于反照率和镜面值,使用默认的纹理(每分量8位浮点数)就够了。

GLuint gBuffer;
glGenFramebuffer(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
GLuint gPosition, gNormal, gColorSpec;
//位置颜色缓冲
glGenTextures(1, &gPosition);
glBindTexture(GL_TEXTURE_2D, gPosition);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCRHEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);//法线颜色缓冲
glGenTextures(1, &gNormal);
glBindTexture(GL_TEXTURE_2D, gNormal);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0);//颜色+镜面颜色缓冲
glGenTextures(1, &gAlbedoSpec);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec, 0);//告诉OpenGL我们将用哪个颜色附件来渲染
GLuint attatchments[3] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
glDrawBuffers(3, attachments);// 之后同样添加渲染缓冲对象(Render Buffer Object)为深度缓冲(Depth Buffer),并检查完整性
[...]

接下来就是将数据渲染到G-Buffer中,我们将使用一下片元着色器

#version 330 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;uniform sampler2D texture_diffuse1;
uniform sampler2D texture_specular1;void main()
{    // 存储第一个G缓冲纹理中的片段位置向量gPosition = FragPos;// 同样存储对每个逐片段法线到G缓冲中gNormal = normalize(Normal);// 和漫反射对每个逐片段颜色gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb;// 存储镜面强度到gAlbedoSpec的alpha分量gAlbedoSpec.a = texture(texture_specular1, TexCoords).r;
}  

因为有光照计算,所以要将所有坐标转换到一个坐标系下,此处我们是将所有坐标转换到世界空间下

三、Lighting Pass

我们通过对G-Buffer进行逐像素的遍历,将其数据作为光照计算的输入,来计算场景最终的光照颜色。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderLightingPass();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gPosition);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gNormal);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);//发送光照相关的uniform
SendAllLightUniformsToShader(shaderLightingPass);
glUniform3fv(glGetUniformLocation(shaderLightingPass.Program, "viewPos"), 1, &camera.Position[0]);
RenderQuad();  

在片元着色器中,我们将会在G-Buffer中直接采样

#version 330 core
out vec4 FragColor;
in vec2 TexCoords;uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;struct Light {vec3 Position;vec3 Color;
};
const int NR_LIGHTS = 32;
uniform Light lights[NR_LIGHTS];
uniform vec3 viewPos;void main()
{             // 从G缓冲中获取数据vec3 FragPos = texture(gPosition, TexCoords).rgb;vec3 Normal = texture(gNormal, TexCoords).rgb;vec3 Albedo = texture(gAlbedoSpec, TexCoords).rgb;float Specular = texture(gAlbedoSpec, TexCoords).a;// 然后和往常一样地计算光照vec3 lighting = Albedo * 0.1; // 硬编码环境光照分量vec3 viewDir = normalize(viewPos - FragPos);for(int i = 0; i < NR_LIGHTS; ++i){// 漫反射vec3 lightDir = normalize(lights[i].Position - FragPos);vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Albedo * lights[i].Color;lighting += diffuse;}FragColor = vec4(lighting, 1.0);
}  

四、结合延迟渲染和前向渲染

在延迟渲染中,光源通常被视为无形的点或方向,而不是具有材质和颜色的物体。如果我们想将光源渲染为一个带有光照颜色的立方体,就需要额外的几何处理,而这超出了延迟渲染的范畴,就需要结合前向渲染(透明物体、镜面反射、光源模型等)。

前向渲染的部分会在延迟渲染操作之后进行。

// 延迟渲染光照渲染阶段
[...]
RenderQuad();// 现在像正常情况一样正向渲染所有光立方体
shaderLightBox.Use();
glUniformMatrix4fv(locProjection, 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(locView, 1, GL_FALSE, glm::value_ptr(view));
for (GLuint i = 0; i < lightPositions.size(); i++)
{model = glm::mat4();model = glm::translate(model, lightPositions[i]);model = glm::scale(model, glm::vec3(0.25f));glUniformMatrix4fv(locModel, 1, GL_FALSE, glm::value_ptr(model));glUniform3fv(locLightcolor, 1, &lightColors[i][0]);RenderCube();
}

在这里插入图片描述
不过现在的深度结果并不正确,因为除了光源立方体的深度信息都在延迟渲染过程中,所以我们需要将延迟渲染中的深度信息提取出来,然后再渲染光立方体。

我们可以使用glBlitFramebuffer复制一个帧缓冲的内容到另一个帧缓冲中。我们需要指定一个帧缓冲为读帧缓冲(Read Framebuffer),并且类似地指定一个帧缓冲为写帧缓冲(Write Framebuffer)

glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // 写入到默认帧缓冲
glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST
);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 现在像之前一样渲染光立方体
[...]  

在这里我们复制整个读帧缓冲的深度缓冲信息到默认帧缓冲的深度缓冲,对于颜色缓冲和模板缓冲我们也可以这样处理。

在这里插入图片描述

五、更多光源

延迟渲染本身并不能支持非常大量的光源,但是我们可以为其引入一个优化:光体积(Light Volumes)。因为对于场景中的物体,有的光是影响非常非常小的,所以我们就可以计算光的影响半径。

我们可以通过光源的衰减值,来计算光的影响范围,我们只需要对在光影响范围内的片段进行光照计算就可以了。

相关文章:

LearnOpenGL——延迟渲染学习笔记

延迟渲染学习笔记 一、基本概念二、G-BufferMRT 三、Lighting Pass四、结合延迟渲染和前向渲染五、更多光源 我们之前使用的一直是 前向渲染&#xff08;正向渲染 Forward Rendering&#xff09;&#xff0c;指的是在场景中根据所有光源照亮一个物体&#xff0c;之后再渲染下一…...

惠海H4312 dcdc同步整流降压恒压IC 30V 40V转3.3V/5V/12V小体积大电流单片机供电

1.产品描述 H4312是一种内置30V耐压MOS&#xff0c;并且能够实现精确恒压以及恒流的同步降压型 DC-DC 转换器: 支持 3.1A 持续输出电流输出电压可调&#xff0c;最大可支持 100%占空比;通过调节FB 端口的分压电阻&#xff0c;可以输出2.5V到 24V的稳定电压。 H4312 采用高端…...

[Linux]如何在虚拟机安装Ubuntu?(小白向)

一、我们为什么要在虚拟机中安装Ubuntu? 在虚拟机中安装系统主要是为了让一个系统与我们原本的系统隔离&#xff0c;不管是想运行一些不安全的软件&#xff0c;或者是想运行一些独特的操作系统&#xff0c;我们都可以选择使用虚拟机来安装和隔离这些操作系统。如果你是一位Lin…...

keepalived详解

概念 keepalived 是一款基于 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff0c;虚拟路由冗余协议&#xff09;协议来实现高可用&#xff08;High Availability, HA&#xff09;的轻量级软件。它主要用于防止单点故障&#xff0c;特别是在 Linux 环境下&#xff…...

工业设备中弧形导轨的检测标准是什么?

弧形导轨在工业自动化中扮演着重要的角色&#xff0c;‌尤其是在需要曲线运动或圆弧插补的场合。这种运动形式在工业自动化中虽然不如直线运动普遍&#xff0c;‌但在某些特定应用中却是不可或缺的。弧形导轨的质量直接影响加工效率与加工质量&#xff0c;因此&#xff0c;弧形…...

Redis 技术详解

一、Redis 基础 &#xff08;一&#xff09;为什么使用 Redis 速度快&#xff0c;因为数据存在内存中&#xff0c;类似于 HashMap&#xff0c;查找和操作的时间复杂度都是 O(1)。支持丰富数据类型&#xff0c;支持 string、list、set、Zset、hash 等。支持事务&#xff0c;操…...

Kubernetes Pod入门

在 Kubernetes 中&#xff0c;一个重要的概念就是 Pod(豆英)&#xff0c;Kubernetes 并不是直接管理容器的&#xff0c;他的最小管理单元叫做 Pod。 一、什么是 Pod。 Pod 是一个或多个容器的组合。这些容器共享存储、网络和命名空间&#xff0c;以及运行规范。在 Pod中&…...

opencv批量修改图片大小

文章已删除&#xff0c;访问可以 在点击这里查找. 在点击这里查找. 在点击这里查找. 在点击这里查找. 在点击这里查找. 在点击这里查找. 在点击这里查找. ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~...

【RTT-Studio】详细使用教程十二:UART的分析和使用

文章目录 一、简介1.串口发送模式2.串口接收模式 二、串口配置三、串口发送四、串口接收 一、简介 本文主要阐述STM32串口的几种工作中使用的工作模式和编程思路。串口通常情况下使用的是&#xff1a;1个起始位&#xff0c;8个数据位&#xff0c;无奇偶校验&#xff0c;1位停止…...

【AI绘画】Midjourney前置指令/settings设置详解

文章目录 &#x1f4af;Midjourney前置指令/settings设置详解&#x1f4af;Use the default model&#xff08;AI绘画所使用的大模型&#xff09;Midjourney Model&#xff08;Midjourney 模型&#xff09;Niji Model&#xff08;Niji模型&#xff09; &#x1f4af;Midjourney…...

【NI国产替代】PXIe‑4330国产替代24位,8通道PXI应变/桥输入模块

25 kS/s&#xff0c;24位&#xff0c;8通道PXI应变/桥输入模块 PXIe‑4330是一款同步输入模块&#xff0c;为基于桥接的传感器提供集成数据采集和信号调理。 PXIe‑4330具有更高的准确性、高数据吞吐量和同步特性&#xff0c;使其成为高密度测量系统的理想选择。\n\n为了消除噪…...

哪里可以免费上传招生简章

随着招生季的临近&#xff0c;各高校和培训机构纷纷摩拳擦掌&#xff0c;准备迎接新一代学子们的到来。在这个信息化的时代&#xff0c;如何让招生简章发挥最大的效用&#xff0c;成为吸引优质生源的关键。 那么如何制作招生简章&#xff1f; 1. 注册账号&#xff1a;访问FLBO…...

Midjourney中文版教程:参数详解

1.长宽比 可以设置图片的纵横比。按照需求可以选择不同的尺寸&#xff0c;也可以自定义。 注意&#xff1a;--ar必须使用整数。使用139&#xff1a;100代替1.39&#xff1a;1。 长宽比会影响生成图像的形状和构图。 在放大时&#xff0c;某些长宽比可能会稍微改变。 较旧的…...

误闯机器学习(第一关-概念和流程)

以下内容&#xff0c;皆为原创&#xff0c;实属不易&#xff0c;请各位帅锅&#xff0c;镁铝点点赞赞和关注吧&#xff01; 好戏开场了。 一.什么是机器学习 机器学习就是从数据中自动分析获取模型&#xff08;总结出的数据&#xff09;&#xff0c;并训练模型&#xff0c;去预…...

Tensorflow 2.16.0+在PyCharm中找不到keras的报错解决

在PyCharm(2024.2版本)中&#xff0c;直接使用from tensorflow import keras会提示“Cannot find reference ‘keras’ in ‘init.py’ ”&#xff0c;找不到keras&#xff0c;如下图所示。 查阅相关资料&#xff0c;可以发现在tf2.16之后&#xff0c;默认的keras后端升级为了…...

【Python】高效的Web自动化测试利器—Python+Playwright快速上手自动化实战指南(限时开放)

文章目录 前言一.playwright是什么二.python引入playwright1.安装2.playwright命令行参数3.playwright codegen自动生成代码4.Chrome和Chromium有什么关系?三.基本概念1. 无头浏览器(Headless Browser)2.同步和异步模式操作playwright2.1.同步(Sync)模式同步方式代码模板2…...

CentOS上安装和配置Docker与Docker Compose的详细指南

引言 大家好&#xff0c;我是小阳&#xff0c;在这篇文章中&#xff0c;我将带大家一步步完成在CentOS系统上安装和配置Docker与Docker Compose的过程。通过这篇详细的指南&#xff0c;你将能够轻松配置Docker环境&#xff0c;并在日常开发和部署中享受其带来的便利。 原文阅…...

Vim多文件操作

Vim多文件编辑的实际意义在于它极大地提高了开发者在处理多个相关文件时的效率和便利性。在软件开发、文本编辑、代码审查、配置管理等场景中&#xff0c;经常需要同时打开和操作多个文件。Vim的多文件编辑功能使得这些任务变得更加直观和高效。 提高编码效率&#xff1a;在开发…...

【ARM+Codesys 客户案例 】 基于RK3568/A40i/STM32+CODESYS在智能制造中的应用案例:全自动切片机器人

蔬菜是人们日常生活必不可缺的食品&#xff0c;并且食用方法多种多样。自步入小康社会以来&#xff0c;人们的生活节奏越来越快&#xff0c;很多传统服务已不能满足人们的物质需求和生活节奏。日常生活中通过手工快速切菜严重地威胁着人身安全&#xff0c;切菜时间过长或切菜不…...

NSI程序打包脚本文件编写教程

引言 NSIS (Nullsoft Scriptable Install System) 是一个专业开源的制作 windows 安装程序的工具。我们通过HM NSIEDIT编写好脚本、编译即可生成exe安装包。安装过程中可以配置其安装包图标、名称、出版人、网站等。此外&#xff0c;还可以设置程序开机自启动、管理员权限运行…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...