【OpenGL】延迟着色和G缓冲

延迟着色(Deferred Shading)是一种渲染技术,通过将几何信息(如位置、法线和颜色)存储在缓冲区中,分两步完成光照计算,从而提升复杂场景的性能。

延迟着色的核心步骤

  1. 几何阶段 (Geometry Pass)

    • 渲染场景,将每个像素的几何信息(世界空间位置、法线、颜色等)写入帧缓冲区中的多个纹理(称为 G 缓冲区,G-Buffer)。
  2. 光照阶段 (Lighting Pass)

    • 使用 G 缓冲区中的数据,计算每个像素的光照。
    • 只在屏幕空间计算光照,减少计算开销。
  3. 最终合成 (Final Pass)

    • 将光照阶段的输出与其他效果(如后期处理)结合,最终输出到屏幕。

延迟着色代码框架

1. 初始化 G 缓冲区

unsigned int gBuffer;
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);unsigned int gPosition, gNormal, gAlbedoSpec;// 位置缓冲
glGenTextures(1, &gPosition);
glBindTexture(GL_TEXTURE_2D, gPosition);
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_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_UNSIGNED_BYTE, 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);// 设置帧缓冲区的颜色附件
unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);// 深度缓冲
unsigned int rboDepth;
glGenRenderbuffers(1, &rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)std::cout << "G-Buffer Framebuffer not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);

详解:

创建帧缓冲对象 (Framebuffer Object, FBO)

unsigned int gBuffer;
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
  • glGenFramebuffers(1, &gBuffer);

    • 创建一个帧缓冲对象 (Framebuffer),用于离屏渲染。
    • gBuffer 是帧缓冲对象的 ID。
  • glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);

    • gBuffer 绑定为当前的帧缓冲对象,接下来的所有渲染操作都会输出到这个缓冲区,而不是默认的屏幕缓冲

2. 创建 G 缓冲区中的纹理附件 (Textures as Attachments)

2.1. 创建位置缓冲区
glGenTextures(1, &gPosition);
glBindTexture(GL_TEXTURE_2D, gPosition);
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_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);
  • glGenTextures(1, &gPosition);

    • 创建一个纹理对象,用于存储顶点的 世界空间位置
    • gPosition 是纹理的 ID。
  • glBindTexture(GL_TEXTURE_2D, gPosition);

    • gPosition 绑定为当前激活的 2D 纹理目标,后续配置均应用到这个纹理。
  • glTexImage2D(...)

    • 创建一个 2D 纹理,并分配内存空间:
      • GL_RGB16F:每个像素存储 3 个分量(RGB),16 位浮点数,用于精确表示顶点位置。
      • SCR_WIDTHSCR_HEIGHT:纹理分辨率,通常为屏幕尺寸。
      • GL_RGB:数据格式,表示三个通道。
      • GL_FLOAT:纹理数据类型,浮点数。
  • glTexParameteri(...)

    • 设置纹理采样参数:
      • GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER:指定纹理缩小和放大时的采样方式,这里使用 GL_NEAREST(最近邻采样)保证不模糊。
  • glFramebufferTexture2D(...)

    • gPosition 纹理附加到帧缓冲的 GL_COLOR_ATTACHMENT0 槽位,用于存储位置数据。

2.2. 创建法线缓冲区

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);

这部分代码与 gPosition 的流程类似,但 gNormal 用于存储 顶点法线,格式和精度相同 (GL_RGB16F)。

2.3. 创建颜色 + 镜面强度缓冲区

glGenTextures(1, &gAlbedoSpec);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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);
  • GL_RGBA

    • 该纹理存储颜色数据(RGB)和镜面强度(Alpha 通道)。
    • 数据类型为 GL_UNSIGNED_BYTE,精度为 8 位无符号整数。
  • 作用

    • RGB 保存像素颜色信息(漫反射颜色)。
    • A 保存镜面反射强度,用于光照计算。
  • GL_COLOR_ATTACHMENT2

    • 将纹理附加到帧缓冲的第三个颜色附件。

3. 设置多个颜色附件

unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);

glDrawBuffers(...)

  • 告诉 OpenGL 渲染时将输出数据写入哪些颜色附件。
  • GL_COLOR_ATTACHMENT0 对应位置,GL_COLOR_ATTACHMENT1 对应法线,GL_COLOR_ATTACHMENT2 对应颜色+镜面反射强度。

4. 深度缓冲区

unsigned int rboDepth;
glGenRenderbuffers(1, &rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);
  • glGenRenderbuffers(1, &rboDepth);

    • 创建一个渲染缓冲对象 (Renderbuffer Object),用于存储深度信息。
  • glRenderbufferStorage(...)

    • 分配渲染缓冲区的存储空间:
      • GL_DEPTH_COMPONENT:只存储深度信息。
      • 分辨率为屏幕大小。
  • glFramebufferRenderbuffer(...)

    • 将渲染缓冲附加到帧缓冲的深度附件 (GL_DEPTH_ATTACHMENT),用于深度测试。

5. 检查帧缓冲完整性

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)std::cout << "G-Buffer Framebuffer not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
  • glCheckFramebufferStatus(...)

    • 检查帧缓冲是否完整。若返回值不是 GL_FRAMEBUFFER_COMPLETE,则配置存在问题。
  • glBindFramebuffer(GL_FRAMEBUFFER, 0);

    • 解除帧缓冲绑定,恢复默认的屏幕缓冲。

2. 几何阶段 (Geometry Pass) 在几何阶段,渲染场景,将几何信息存储到 G 缓冲区。

顶点着色器(geometry_vertex_shader.glsl):

#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoords;out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;TexCoords = aTexCoords;gl_Position = projection * view * vec4(FragPos, 1.0);
}

片段着色器(geometry_fragment_shader.glsl):

#version 330 core
layout(location = 0) out vec3 gPosition;
layout(location = 1) out vec3 gNormal;
layout(location = 2) out vec4 gAlbedoSpec;in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;uniform sampler2D texture_diffuse;
uniform sampler2D texture_specular;void main()
{gPosition = FragPos;gNormal = normalize(Normal);vec3 albedo = texture(texture_diffuse, TexCoords).rgb;float spec = texture(texture_specular, TexCoords).r;gAlbedoSpec = vec4(albedo, spec);
}

3. 光照阶段 (Lighting Pass) 使用屏幕空间四边形渲染光照。

片段着色器(lighting_fragment_shader.glsl):

#version 330 core
out vec4 FragColor;in vec2 TexCoords;uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;uniform vec3 viewPos;
uniform vec3 lightPos;
uniform vec3 lightColor;void main()
{// 从 G 缓冲区中读取数据vec3 FragPos = texture(gPosition, TexCoords).rgb;vec3 Normal = normalize(texture(gNormal, TexCoords).rgb);vec3 Albedo = texture(gAlbedoSpec, TexCoords).rgb;float Specular = texture(gAlbedoSpec, TexCoords).a;// 光照计算vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(Normal, lightDir), 0.0);vec3 diffuse = diff * lightColor;vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, Normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16.0);vec3 specular = Specular * spec * lightColor;vec3 ambient = 0.1 * Albedo;vec3 result = (ambient + diffuse + specular) * Albedo;FragColor = vec4(result, 1.0);
}

在主循环中渲染光照:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);lightingShader.use();
lightingShader.setVec3("viewPos", camera.Position);
lightingShader.setVec3("lightPos", lightPos);
lightingShader.setVec3("lightColor", lightColor);glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gPosition);
lightingShader.setInt("gPosition", 0);glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gNormal);
lightingShader.setInt("gNormal", 1);glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
lightingShader.setInt("gAlbedoSpec", 2);// 绘制屏幕四边形
renderQuad();

4. 最终合成

根据需要添加后期处理,直接在 FragColor 上进行额外处理即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/480472.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3 ajax获取json数组排序举例

使用axios获取接口数据 可以在代码中安装axios包&#xff0c;并写入到package.json文件&#xff1a; npm install axios -S接口调用代码举例如下&#xff1a; const fetchScore async () > {try {const res await axios.get(http://127.0.0.1:8000/score/${userInput.v…

详解登录MySQL时出现SSL connection error: unknown error number错误

目录 登录MySQL时出错SSL connection error: unknown error number 出错原因 使用MySQL自带的工具登录MySQL 登陆之后&#xff0c;使用如下命令进行查看 解决方法 找到MySQL8安装目录下的my.ini配置文件 记事本打开my.ini文件&#xff0c;然后按下图所示添加配置 此时再…

mini-spring源码分析

IOC模块 关键解释 beanFactory&#xff1a;beanFactory是一个hashMap, key为beanName, Value为 beanDefination beanDefination: BeanDefinitionRegistry&#xff0c;BeanDefinition注册表接口&#xff0c;定义注册BeanDefinition的方法 beanReference&#xff1a;增加Bean…

linux内核读写硬盘文件 kernel_writekernel_read

简介 在内核中读取硬盘文件&#xff0c;内核5.10测试了下&#xff0c;可以正常运行。 代码 1. 使用filp_open打开文件 2. 使用kernel_write和kernel_read读写文件 3. 使用filp_close关闭文件 #include <linux/module.h> #include <linux/init.h> #include &l…

记录一次 k8s 节点内存不足的排查过程

背景&#xff1a;前端服务一直报404&#xff0c;查看k8s日志&#xff0c;没发现报错&#xff0c;但是发现pods多次重启。 排查过程&#xff1a; 查看pods日志&#xff0c;发现日志进不去。 kubectrl logs -f -n weave pod-name --tail 100查看pod describe kubectl describ…

微软要求 Windows Insider 用户试用备受争议的召回功能

拥有搭载 Qualcomm Snapdragon 处理器的 Copilot PC 的 Windows Insider 计划参与者现在可以试用 Recall&#xff0c;这是一项臭名昭著的快照拍摄 AI 功能&#xff0c;在今年早些时候推出时受到了很多批评。 Windows 营销高级总监 Melissa Grant 上周表示&#xff1a;“我们听…

嵌入式Qt使用ffmpeg视频开发记录

在此记录一下Qt下视频应用开发的自学历程&#xff0c;可供初学者参考和避雷。 了解常用音频格式yuv420p、h264等了解QML&#xff0c;了解QVideoOutput类的使用&#xff0c;实现播放yuv420p流参考ffmpeg官方例程&#xff0c;调用解码器实现h264解码播放 不需要手动分帧。ffmpeg…

Web day02 Js Vue Ajax

目录 1.javascript: 1.js的引入方式&#xff1a; 2.js变量 & 数据类型 & 输出语句&#xff1a; 模板字符串&#xff1a; 3.函数 & 自定义对象&#xff1a; 4. json 字符串 & DOM操作&#xff1a; 5. js事件监听&#xff1a; 6.js的模块化导入或者导出&a…

Qt Graphics View 绘图实例

Qt Graphics View 绘图实例 这个实例程序实现如下功能&#xff1a; 可以创建矩形、椭圆、三角形、梯形、直线、文字等基本图形。每个图形项都可以被选择和移动。图形项或整个视图可以缩放和旋转。图形项重叠时&#xff0c;可以调整前置或后置。多个图形项可以组合&#xff0c;…

TCP socket api详解 续

文章目录 守护进程怎么做到&#xff1f;setsid返回值 dev/null字符文件 daemonTCP协议 退出的时候呢&#xff1f; 会话有很多后台任务&#xff0c;bash肯定会退&#xff0c;那后台会话怎么办呢&#xff1f; 理论上也要退的&#xff0c;但实际上关了bash&#xff0c;bash肯定要…

06_数据类型

数据类型 数据类型分类 JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。(ES6 又新增了第七种 Symbol 类型的值和第八种 BigInt类型,当前课程暂不涉及) 据类型分类 原始类型(基础类型) var age = 20, var name = 尚学堂"; var le…

scrapy豆瓣爬虫增强-批量随机请求头

1.1 豆瓣爬虫增强,中间件随机请求头 1.2 清除原有的中间件,进行中间件测试 1.3 导入全新的中间件 1.4 运行爬虫,这个时候的请求头是固定的 1.5 强化对agent的输出,会舍弃输出cookie,使输出更明了 1.6 转移输出请求头位置 新增输出 造成这样问题的原因是Douban/Douban/settings…

非关系型数据库有哪些特点?

非关系型数据库&#xff08;NoSQL&#xff09;具有以下主要特点‌&#xff1a;‌1 ‌灵活的数据存储方式‌&#xff1a;非关系型数据库不采用传统的基于表格的数据存储方式&#xff0c;而是采用更加灵活的数据存储方式。它可以存储各种类型的数据&#xff0c;包括文本、图像、音…

智慧防汛平台在城市生命线安全建设中的应用

随着城市化进程的加快&#xff0c;城市基础设施的复杂性和互联性不断增强&#xff0c;城市生命线的安全管理面临前所未有的挑战。智慧防汛平台作为城市生命线安全建设的重要组成部分&#xff0c;通过现代信息技术提升城市防汛应急管理的智能化水平&#xff0c;保障城市安全。 …

【ChatGPT大模型开发调用】如何获得 OpenAl API Key?

如何获取 OpenAI API Key 获取 OpenAI API Key 主要有以下三种途径&#xff1a; OpenAI 官方平台 (推荐): 开发者用户可以直接在 OpenAI 官方网站 (platform.openai.com) 注册并申请 API Key。 通常&#xff0c;您可以在账户设置或开发者平台的相关页面找到申请入口。 Azure…

vue3 发送 axios 请求时没有接受到响应数据

<script setup> import Edit from ./components/Edit.vue import axios from axios import { onMounted,ref } from vue// TODO: 列表渲染 //装数据的列表 const list ref([]) const count ref(0) const getList async () > {//通过发送 /list 请求从后端拿到列表数…

衡山派D133EBS 开发环境安装及SDK编译烧写镜像烧录

1.创建新文件夹&#xff0c;用来存放SDK包&#xff08;其实本质就是路径要对就ok了&#xff09;&#xff0c;右键鼠标通过Open Git Bash here来打开git 输入命令 git clone --depth1 https://gitee.com/lcsc/luban-lite.git 来拉取&#xff0c;如下所示&#xff1a;&#xff0…

关于Vscode配置Unity环境时的一些报错问题(持续更新)

第一种报错&#xff1a; 下载net请求超时&#xff08;一般都会超时很正常的&#xff09; 实际时并不需要解决&#xff0c;它对你的项目毫无影响 第二种报错&#xff1a; .net版本不匹配 解决&#xff1a;&#xff08;由于造成问题不一样&#xff0c;所以建议都尝试一次&…

快速理解微服务中Fegin的概念

一.由来 1.在传统的架构里面&#xff0c;我们是通过使用RestTemplate来访问其他的服务&#xff0c;但是这种方式就存在了一个很大的缺陷&#xff0c;也就是被调用方如果发生了服务的迁移(IP和端口发生了变化)&#xff0c;那么调用方也需要同步的在代码里面进行修改&#xff0c;…

【网络安全 | 漏洞挖掘】绕过SAML认证获得管理员面板访问权限

未经许可,不得转载。 文章目录 什么是SAML认证?SAML是如何工作的?SAML响应结构漏洞结果什么是SAML认证? SAML(安全断言标记语言)用于单点登录(SSO)。它是一种功能,允许用户在多个服务之间切换时无需多次登录。例如,如果你已经登录了facebook.com,就不需要再次输入凭…