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

【OpenGL】LearnOpenGL学习笔记18 - Uniform缓冲对象UBO

上接:https://blog.csdn.net/weixin_44506615/article/details/150962393?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl | https://github.com/Duo1J/LearnOpenGL

在接触Uniform缓冲对象之前,我们先来扩充一些数据和GLSL方面的知识

一、glBufferSubData

我们之前使用 glBufferData 来为缓冲区填充数据,它一次性会填充整个缓冲区,如果我们想为缓冲区的特定区域进行填充,也就是填充一部分,就可以用到 glBufferSubData

// 填充范围 [24, 24 + sizeof(data)]
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data);

二、glMapBuffer

我们还可以直接获取到缓冲指针来直接操作缓冲

float data[] = {...
};glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 获取指针
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// 复制数据到缓冲
memcpy(ptr, data, sizeof(data));
// 归还指针
glUnmapBuffer(GL_ARRAY_BUFFER);

三、分批顶点属性

目前我们顶点属性的排布是进行了交错处理的,例如位置-法线-UV我们是按1112223311122233这样来排列
而我们在加载模型之后获得的三种数据往往是分离的,如果不想手动进行交错组装,那么我们可以采用分批的方式进行排列:1111112222223333

float positions[] = { ... };
float normals[] = { ... };
float uvs[] = { ... };glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(uvs), &uvs);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);  
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions)));  
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));

不管使用交错还是分批的方式进行排布都是可行的,不过还是推荐使用交错方法,这样一来每个顶点着色器运行的时候所需要的顶点属性在内存中会更加紧密的对齐

四、glCopyBufferSubData

我们可以通过 glCopyBufferSubData 来将一个缓冲的数据复制到另一个缓冲中

// readtarget 复制源
// writetarget 复制目标
// readoffset 读偏移
// writeoffset 写偏移
// 复制大小
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));

五、GLSL内置变量

1. gl_PointSize
顶点着色器中使用
在图元是GL_POINTS的时候,我们可以通过gl_PointSize来设置渲染点的大小

glEnable(GL_PROGRAM_POINT_SIZE);
void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);    gl_PointSize = gl_Position.z;    
}

当我们离点越远,它们会变得更大
gl_PointSize

2. gl_VertexID
顶点着色器中使用
当使用 glDrawElements 进行绘制的时候,这个变量会存储正在绘制顶点的索引
当使用 glDrawArrays 进行绘制的时候,这个变量会存储从渲染调用开始的已处理顶点的数量

3. gl_FragCoord
片段着色器中使用
gl_FragCoord 是一个vec3向量,其x、y表示该片段在屏幕空间中的坐标,z表示深度
通过x、y我们可以实现像是左边显示法线、右边显示渲染结果这样的效果 (在一些画面对比、技术演示中常用到)

if (gl_FragCoord.x >= 400)FragColor = vec4(result, 1.0);
elseFragColor = vec4(normal, 1.0);

gl_FragCoord
4. gl_FrontFacing
片段着色器中使用
gl_FrontFacing 是一个bool类型变量,它可以告诉我们当前片段是属于正向面还是反向面的一部分,不过如果开了面剔除那就没有意义了

5. gl_FragDepth
片段着色器中使用
gl_FragCoord.z 可以获得深度值,而 gl_FragDepth 可以设置深度值,如果我们没有主动设置的话,它会自动取用 gl_FragCoord.z 的值

// 设置该片段深度值为0
gl_FragDepth = 0.0;

如果我们设置了深度值,OpenGL会禁用所有的提前深度测试,这是因为它无法在片段着色器之前就知道片段的深度值
不过在OpenGL 4.2 起,我们可以通过设置深度条件来达到

// condition:
// any: 默认值,提前深度测试是禁用的,你会损失很多性能
// greater: 你只能让深度值比 gl_FragCoord.z 更大
// less: 你只能让深度值比 gl_FragCoord.z 更小
// unchanged: 如果你要写入gl_FragDepth,你将只能写入gl_FragCoord.z的值
layout (depth_<condition>) out float gl_FragDepth;

六、接口块

之前我们从顶点着色器向片段着色器发送数据都是使用的 out XXX xx 这样来定义的,GLSL还提供了一种叫 接口块 (Interface Block) 的东西,它类似结构体,可以让我们成块的输出和输入

// 顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;// 声明输出接口块
out VS_OUT
{vec2 TexCoords;
} vs_out;void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);    vs_out.TexCoords = aTexCoords;
}  // 片段着色器
#version 330 core
out vec4 FragColor;// 声明同名输入接口块
in VS_OUT
{vec2 TexCoords;
} fs_in;uniform sampler2D texture;void main()
{             FragColor = texture(texture, fs_in.TexCoords);   
}

Uniform缓冲对象 (UBO)

一些扩充知识介绍完,终于到了Uniform缓冲对象 (UBO) 了
到目前为止,我们创建的不同Shader,每一个需要用到VP矩阵的Shader都需要单独去传入uniform变量
而UBO则可以让我们定义在多个着色器程序相同全局Uniform变量

UBO仍然是一个缓冲对象,所以我们可以通过 glGenBuffers 来创建它,并把它绑定到 GL_UNIFORM_BUFFER 缓冲目标上,对于用到的着色器中,我们需要像以下这样编写

#version 330 core
layout (location = 0) in vec3 aPos;layout (std140) uniform Matrices
{mat4 projection;mat4 view;
};uniform mat4 model;void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);
}

我们声明了一个叫做MatricesUniform块,但是我们访问时不需要带上块名
对于 layout (std140) 则表示该块用到的Uniform块布局

Uniform块布局就是Uniform变量在缓冲中的排列方式,除此之外还有 sharedpackedstd430 等,详情可以看这里

绑定点 (Binding Point)

目前,我们可以像这样创建并绑定UBO

unsigned int uboExampleBlock;
glGenBuffers(1, &uboExampleBlock);
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
// 分配152字节的内存
glBufferData(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); 
glBindBuffer(GL_UNIFORM_BUFFER, 0);

但是我们要怎样将Shader中声明的Uniform块和UBO联系起来呢? 这就要用到绑定点
在OpenGL上下文中定义了一系列的绑定点,我们可以将一个UBO与它链接起来,并将Shader中的Uniform块与它链接起来,如下图所示 (图片来自于LearnOpenGL)
绑定点
可以像这样链接Uniform块和绑定点

// 将shader中的Matrices Uniform块绑定到绑定点1
unsigned int matrices_idx = glGetUniformBlockIndex(shader.ProgramID, "Matrices");   
glUniformBlockBinding(shader.ProgramID, matrices_idx, 1);

在 OpenGL 4.2 版本起,可以通过布局标识符来链接

layout(std140, binding = 1) uniform Matrices { ... };

然后链接UBO和绑定点

// (绑定目标, 绑定点, 缓冲)
glBindBufferBase(GL_UNIFORM_BUFFER, 1, uboExampleBlock); 
// 或
// (绑定目标, 绑定点, 缓冲, 偏移, 大小)
glBindBufferRange(GL_UNIFORM_BUFFER, 1, uboExampleBlock, 0, 152);

使用Uniform缓冲对象

接下来可以修改一下我们的代码,将VP矩阵移到UBO中

首先修改着色器,我们需要修改所有需要用到UBO的顶点着色器,这里以背包为例
VertexShader.glsl

#version 330 corelayout (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;
out vec3 Position;uniform mat4 model;
// 声明Uniform块
layout (std140) uniform Matrices
{mat4 view;mat4 projection;
};void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;TexCoords = aTexCoords;Position = vec3(model * vec4(aPos, 1.0));
}

接下来创建UBO缓冲,预留 2 * sizeof(glm::mat4) 大小的空间,并链接到绑定点0
Main.cpp

unsigned int uboMaterices;
glGenBuffers(1, &uboMaterices);
glBindBuffer(GL_UNIFORM_BUFFER, uboMaterices);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMaterices, 0, 2 * sizeof(glm::mat4));

然后链接Uniform块与绑定点

// 定义宏辅助
#define BindMatericesBlock(NAME,SHADER_VAR, SLOT) unsigned int NAME##MatricesUniformBlockIdx = glGetUniformBlockIndex(SHADER_VAR.ProgramID, "Materices"); \
glUniformBlockBinding(SHADER_VAR.ProgramID, NAME##MatricesUniformBlockIdx, SLOT);BindMatericesBlock(bag, shader, 0);
BindMatericesBlock(reflect, reflectShader, 0);
BindMatericesBlock(refract, refractShader, 0);
BindMatericesBlock(edge, edgeShader, 0);
BindMatericesBlock(grass, grassShader, 0);
BindMatericesBlock(window, windowShader, 0);

最后,更新数据到UBO

// 主循环
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::mat4(1);
projection = glm::perspective(glm::radians(camera.fov), screenWidth / screenHeight, camera.near, camera.far);glBindBuffer(GL_UNIFORM_BUFFER, uboMaterices);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(view));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(projection));
glBindBuffer(GL_UNIFORM_BUFFER, 0);

编译运行,检查我们所看见的图像,它应该和之前没有任何差别
稍微整理一下代码,完整代码可在顶部git仓库中找到

http://www.dtcms.com/a/360396.html

相关文章:

  • [每周一更]-(第158期):构建高性能数据库:MySQL 与 PostgreSQL 系统化问题管理与优化指南
  • XPlayer播放器APP:安卓平台上的全能视频播放器
  • 网络代理协议深度对比
  • Linux/UNIX系统编程手册笔记:系统和进程信息、文件I/O缓冲、系统编程概念以及文件属性
  • Multi-Head RAG: Solving Multi-Aspect Problems with LLMs
  • ST-2110概述
  • MySQL专题Day(1)————事务
  • postman 用于接口测试,举例
  • Linux shell 脚本基础 003
  • centos7安装jdk17
  • c++程序员日常超实用工具(长期记录更新)
  • PS自由变换
  • 【人工智能99问】LLaMA中的RoPE是什么?(35/99)
  • 学习Python中Selenium模块的基本用法(12:操作Cookie)
  • 【系统分析师】高分论文:论大数据架构的应用
  • 写一个 RTX 5080 上的 cuda gemm fp16
  • 使用yt-dlp下载网页视频
  • synchronized的锁对象 和 wait,notify的调用者之间的关系
  • Wi-Fi技术——初识
  • Flink NettyBufferPool
  • Docker中使用Compose配置现有网络
  • C语言————深入理解指针1(通俗易懂)
  • Linux 网络编程:深入理解套接字与通信机制
  • 【MySQL自学】SQL语法全解(上篇)
  • Matlab自学笔记六十六:求解带参数的不等式
  • MySQL服务启动命令手册(Linux+Windows+macOS)(下)
  • 盛最多水的容器:双指针法的巧妙运用(leetcode 11)
  • ARM裸机开发(基础汇编指令)Day02
  • [特殊字符] Rust概述:系统编程的革命者
  • Python轻量化革命:用MicroPython构建边缘智能设备