c++ - 统一缓冲区对象中的数组问题
问题描述
我在 OpenGL 方面有半经验,目前正在研究 2D 游戏的简单照明。我的计划如下:
- 创建一个 Uniform Buffer Object 来存储两个 vec4 数组,代表场景中的所有活动灯光:一个用于位置,一个用于颜色。
- 在我的顶点着色器中将该统一缓冲区对象作为一个统一对象(显然)。以某种方式转换数据并将其复制到与制服格式相同的输出接口块(对不起,如果这是令人困惑/不正确的术语,请随时要求澄清)。
- 使用顶点着色器输出的接口块作为我的片段着色器的输入,并使用块的内容执行光照计算。
这是一个简单的任务,但有一个问题:如果我的统一缓冲区中的数组的大小大于 14,我的着色器似乎会默默地失败(编译或链接过程中没有错误字符串),这奇怪地使所有其他制服无法进入。
使用 GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 作为目标调用 glGetIntegerv 返回的值返回 4096,我认为这意味着我应该能够在单个统一缓冲区中存储多达 4096 个浮点数/4 个浮点数/vec4/2 个 vec4s/light = 512 个灯.
这是相关的 C++ 代码(请注意,浮点向量的大小已正确填充 vec4 - 我不认为这是与我正在使用的 std140 布局有关的对齐问题):
// generate some test light data and upload it to a UBO
constexpr uint lightCount = 14;
renderer.GetShader().SetUniform1i("u_LightCount", lightCount);
struct
{
float pos[4 * lightCount] = { 0 };
float color[4 * lightCount] = { 0 };
} lights;
for (uint i = 0; i < 4 * lightCount; i += 4)
{
lights.pos[i + 0] = i * 4;
lights.pos[i + 1] = i * 4;
lights.pos[i + 2] = 100.f;
lights.pos[i + 3] = 1;
lights.color[i + 0] = 1;
lights.color[i + 1] = 0;
lights.color[i + 2] = 0;
lights.color[i + 3] = 1;
}
GLuint ubo = 0;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(lights), &lights, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
uint block = glGetUniformBlockIndex(renderer.GetShader().GetId(), "BlockLight");
GLuint bind = 0;
glUniformBlockBinding(renderer.GetShader().GetId(), block, bind);
我在这里绑定可能会做一些奇怪的事情。着色器工作需要 glBindBufferRange 调用,但我看不出它与 glBindBuffer 调用有何不同。
// update light positions based on scroll wheel and upload to the UBO
lightDistance = math::clamp(lightDistance + engine.gl->GetMouseScroll().y, 0, 255);
for (uint i = 0; i < lightCount; i++)
lights.pos[i * 4 + 2] = lightDistance;
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(lights), &lights, GL_DYNAMIC_DRAW);
glBindBufferRange(GL_UNIFORM_BUFFER, bind, ubo, 0, sizeof(lights));
我的顶点着色器代码:
#version 420 core
layout(location = 0) in vec2 i_Position;
layout(location = 1) in vec2 i_TexCoord;
layout(location = 2) in float i_TexIndex;
uniform int u_LightCount;
uniform vec2 u_Scale;
uniform vec2 u_Camera;
layout (std140) uniform BlockLight
{
vec4 pos[14];
vec4 color[14];
} u_Light;
out vec2 v_TexCoord;
out float v_TexIndex;
out BlockLight
{
vec4 pos[14];
vec4 color[14];
} v_Light;
void main()
{
gl_Position = vec4((i_Position + u_Camera) * u_Scale, 0, 1);
v_TexCoord = i_TexCoord;
v_TexIndex = i_TexIndex;
for(int i = 0; i < u_LightCount; i++)
{
v_Light.pos[i] = u_Light.pos[i] + vec4(u_Camera, 0, 1);
v_Light.color[i] = u_Light.color[i];
}
}
和片段着色器:
#version 330 core
#define PIXEL_SIZE 5
layout(location = 0) out vec4 o_Color;
uniform int u_LightCount;
uniform int u_TextureFrames[32];
uniform vec2 u_Resolution;
uniform sampler2DArray u_Textures[32];
in float v_TexIndex;
in vec2 v_TexCoord;
in BlockLight
{
vec4 pos[14];
vec4 color[14];
} v_Light;
void main()
{
vec2 fragPos = floor((gl_FragCoord.xy - u_Resolution / 2.0) / PIXEL_SIZE);
vec3 lightColor = vec3(0);
for(int i = 0; i < u_LightCount; i++)
{
float x = length(v_Light.pos[i].xy - fragPos) * 2;
float z = v_Light.pos[i].z;
float intensity = (x > z ? 0 : clamp((z - sqrt(z * z - (x - z) * (x - z))) / 255, 0, 1));
lightColor += v_Light.color[i].xyz * intensity;
}
lightColor = clamp(lightColor, vec3(0), vec3(1));
int index = int(v_TexIndex);
o_Color = vec4(lightColor, 1) * texture(u_Textures[index], vec3(v_TexCoord, u_TextureFrames[index]));
}
预期输出(仅在使用 <= 14 个灯时发生): 预期输出
错误输出(将着色器中的一个或多个数组大小更改为大于 14 的值时发生): 错误输出
是否有可能我做错了什么和/或在我的 OpenGL 设置代码中跳过了某些内容?我是否不正确地使用 UBO 功能?它实际上是我以某种方式错过的数据填充/格式错误吗?任何帮助深表感谢。
解决方案
感谢@robthebloke 解决了这个问题。这个问题与我的 UBO 设置无关,而是我如何编写着色器。我在顶点着色器中对 UBO 进行操作,将结果写入另一个缓冲区(因为着色器没有对 UBO 的写访问权限),然后在片段着色器中使用该结果缓冲区。
但是,正如 Rob 指出的那样,可以将多少字节作为输入参数发送到片段着色器,可以通过GL_MAX_FRAGMENT_INPUT_COMPONENTS
. 我的缓冲区超过了这个值,导致整个管道失败(但没有抛出错误,我仍然对此感到恼火)。
解决方案是将 UBO 上的操作推迟到片段着色器,方法是将顶点着色器中的相关值作为单个in vec2
. 这解决了我的问题,我现在可以根据需要将尽可能多的数据放入 UBO(不超过 指定的限制GL_MAX_UNIFORM_BLOCK_SIZE
)。
最终的 C++ 代码:
// generate some test light data and upload to a UBO
constexpr uint lightCount = 1000;
renderer.GetShader().SetUniform1i("u_LightCount", lightCount);
struct
{
float pos[4 * lightCount] = { 0 };
float color[4 * lightCount] = { 0 };
} lights;
for (uint i = 0; i < 4 * lightCount; i += 4)
{
lights.pos[i + 0] = i * 4;
lights.pos[i + 1] = i * 4;
lights.pos[i + 2] = 100.f;
lights.pos[i + 3] = 1;
lights.color[i + 0] = 1;
lights.color[i + 1] = 0;
lights.color[i + 2] = 0;
lights.color[i + 3] = 1;
}
GLuint ubo = 0;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(lights), &lights, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
uint block = glGetUniformBlockIndex(renderer.GetShader().GetId(), "BlockLight");
GLuint bind = 0;
glUniformBlockBinding(renderer.GetShader().GetId(), block, bind);
...
// inside render loop
lightDistance = math::clamp(lightDistance + engine.gl->GetMouseScroll().y, 0, 255);
for (uint i = 0; i < lightCount; i++)
lights.pos[i * 4 + 2] = lightDistance;
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(lights), &lights, GL_DYNAMIC_DRAW);
glBindBufferRange(GL_UNIFORM_BUFFER, bind, ubo, 0, sizeof(lights));
...
顶点着色器:
#version 330 core
layout(location = 0) in vec2 i_Position;
layout(location = 1) in vec2 i_TexCoord;
layout(location = 2) in float i_TexIndex;
uniform vec2 u_Scale;
uniform vec2 u_Camera;
out vec2 v_Camera;
out vec2 v_TexCoord;
out float v_TexIndex;
void main()
{
gl_Position = vec4((i_Position + u_Camera) * u_Scale, 0, 1);
v_TexCoord = i_TexCoord;
v_TexIndex = i_TexIndex;
v_Camera = u_Camera;
}
片段着色器:
#version 330 core
#define PIXEL_SIZE 5
layout(location = 0) out vec4 o_Color;
uniform int u_LightCount;
uniform int u_TextureFrames[32];
uniform vec2 u_Resolution;
uniform sampler2DArray u_Textures[32];
layout (std140) uniform BlockLight
{
vec4 pos[1000];
vec4 color[1000];
} u_Light;
in float v_TexIndex;
in vec2 v_TexCoord;
in vec2 v_Camera;
void main()
{
vec2 fragPos = floor((gl_FragCoord.xy - u_Resolution / 2.0) / PIXEL_SIZE);
vec3 lightColor = vec3(0);
for(int i = 0; i < u_LightCount; i++)
{
float x = length(u_Light.pos[i].xy + v_Camera - fragPos) * 2;
float z = u_Light.pos[i].z;
float intensity = (x > z ? 0 : clamp((z - sqrt(z * z - (x - z) * (x - z))) / 255, 0, 1));
lightColor += u_Light.color[i].xyz * intensity;
}
lightColor = clamp(lightColor, vec3(0), vec3(1));
int index = int(v_TexIndex);
o_Color = vec4(lightColor, 1) * texture(u_Textures[index], vec3(v_TexCoord, u_TextureFrames[index]));
}
再次感谢 @robthebloke 提供解决方案并感谢 @Rabbid76 纠正我对 value 的误解GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB
。
推荐阅读
- c# - Picturebox 阻止 MouseEnter 事件触发?
- c# - C# Voting Simulator(如何计算零或未投票的项目/国家)
- android - 如果 TextInputLayout 有显示错误,按钮应该禁用它的活动
- xamarin.forms - 使用 Visual Studio for Mac 创建 Xamarin.Forms 项目时找不到“使用 .NET Standard”
- vue.js - 如何通过 VJ 中的条纹进行苹果支付?
- grid - SyncFusion Grid 的过滤器不起作用,数据未按预期显示
- elasticsearch - 如何使用 Elasticsearch 中的脚本在文档更新中使用聚合值
- javascript - 我想让我的 discord.js 机器人不可见
- android - 旋转矢量测量的解释
- networking - SNMP 32 位和 64 位计数器之间的网络流量差异