我在OpenGL方面有半经验,目前正在为2D游戏开发简单的照明。我的计划如下:
这是一个简单的任务,但有一个问题:如果我的统一缓冲区中的数组大小大于14,我的着色器似乎会默默失败(编译或链接过程中没有错误字符串),这奇怪地使所有其他统一无法访问。
调用glGetIntgerv返回的值GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB作为目标返回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]));
}
预期输出(仅当
错误输出(当将着色器中的一个或多个数组大小更改为值时发生
是否有可能我在OpenGL设置代码中做了一些错误和/或跳过了一些内容?我是否不当地使用了UBO功能?这真的是一个数据填充/格式错误,我不知怎么错过了吗?非常感谢您的帮助。
我处理这些东西已经有一段时间了,所以我相信有人会在这里纠正我,但是GL_MAX_FRAGMENT_INPUT_COMPONENTS报告了什么?
我有一种感觉,这与您的统一缓冲区输入无关,但可能与片段着色器输入有关?14 * 2 * 4 == 112,这似乎非常接近128的默认限制?
一个愚蠢的建议是把灯分成两个均匀的缓冲区?
// in the vertex shader
layout (std140) uniform BlockLightPos
{
vec4 pos[15];
} u_Light_Pos;
in BlockLightPos
{
vec4 pos[15];
} v_Light_pos;
因为您只需修改此处的位置(颜色保持一致):
v_Light_pos.pos[i] = u_Light_Pos.pos[i] + vec4(u_Camera, 0, 1);
所以理论上在片段着色器中你可以做到:
layout (std140) uniform BlockLightColour
{
vec4 color[15];
} u_Light_Colour;
并希望保持在片段输入组件限制内?
虽然现在我刚刚输入了这个,但我意识到u_相机也是一个统一的,所以也许只需将统一移动到片段着色器?(并避免将其作为片段输入传递?)
uniform vec2 u_Camera;
layout (std140) uniform BlockLight
{
vec4 pos[15];
vec4 color[15];
} u_Light;
否则,在片段着色器中指定一个统一的缓冲块,甚至是着色器存储缓冲区?
感谢@robthebloke解决了这个问题。问题与我的UBO设置无关,而是我如何编写着色器。我在顶点着色器中对UBO进行操作,将结果写入另一个缓冲区(因为着色器没有对UBO的写入权限),然后在片段着色器中使用生成的缓冲区。
然而,正如Rob指出的,可以通过GL_MAX_fragment_input_COMPONENTS
将多少字节作为输入参数发送到片段着色器是有限制的。我的缓冲区超过了这个值,导致整个管道失败(但没有抛出错误,对此我仍然感到恼火)。
解决方案是将UBO上的操作推迟到片段着色器,方法是将顶点着色器中的相关值作为vec2中的单个传递给它。这就解决了我的问题,我现在可以把我想要的尽可能多的数据放入UBO(达到
GL\u MAX\u UNIFORM\u BLOCK\u 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纠正了我对值的误解
GL\u MAX\u VERTEX\u UNIFORM\u COMPONENTS\u ARB
。
我正在努力理解这两个,如何使用它们,以及它们是如何联系的。假设我想创建一个简单的地形和一个有纹理的立方体。对于这两个对象,我有三角形、顶点的数组,对于立方体,我有一个包含纹理数据的数组。我的问题是:我如何使用VAOs和VBOs来创建和呈现这两个? null
我正在尝试从ArrayList读取并创建VBO,然后对其进行渲染。问题是我只是渲染了一个空白屏幕。当我立即渲染时,一切都很好;只有现在VBO的才不起作用。 游戏循环如下所示,相机调用glTranslate和glRotate函数。 渲染方法: 阵列列表具有顶点和链接的颜色。每个体素将其顶点添加到ArrayList(而不是立即渲染)。
我有一个JSON对象,我正在将它转换成一个,并在这里进行一些处理。稍后,我想将相同的缓冲区数据转换为有效的JSON对象。 我的工作节点V6.9.1 下面是我尝试过的代码,但当我转换回JSON并且无法打开此对象时,我得到了。 所以我试着用检查的方式打印整个物体 如果我试着像数组一样读取它 我试图解析它也抛出SynTaxError:意外令牌o在JSON在位置2 我需要像我创建的那样将其视为真实对象(我
我目前正在使用LWJGL / OpenGL创建一个2D,自上而下的游戏,但是在绘制实体之后,在使用顶点缓冲区对象渲染实体后,在让它们四处移动的过程中,我遇到了一些问题。这是渲染线程的 run() 方法,以及设置方法: 实体从抽象超类继承这些方法来设置 VBO 并绘制实体(呈现线程中的 drawEntities 方法仅调用该方法,实体更新方法(见下文),而 setUpEntities 调用 setU
问题内容: 在编写用于OpenGL库的Matrix类时,我遇到了一个问题,即使用Java数组还是使用Buffer策略存储数据(JOGL为Matrix操作提供直接缓冲区复制)。为了对此进行分析,我编写了一个小型性能测试程序,该程序比较了Arrays vs Buffers和Direct Buffers上循环和批量操作的相对速度。 我想在这里与您分享我的结果(因为我发现它们很有趣)。请随时发表评论和/或
我有一个使用paramiko的缓冲区的问题,我在这里发现了同样的问题,其中一个解决方案指出: 如果您只是调用.open()来获取SFTPFile实例,而不是使用.get(),那么就对该对象调用.read(),或者只是将其交给Python标准库函数shutil.copyFileObj()来下载内容。这将避免Paramiko预取缓存,并且允许您下载文件,即使它没有那么快。 如果我有: