如何将一个 int 数组发送到我的着色器
How to send an array of int to my shader
我正在制作一个体素引擎,我可以渲染一个块。我正在使用实例化渲染,这意味着我可以通过一次绘制调用来渲染所有块。区块的每个方块都有一个单独的整数(从 0 到 4095),它定义了他的方块类型(0 代表空气,1 代表泥土,等等)。我希望能够通过在我的片段着色器中应用良好的纹理来渲染我的块。我的块包含一个三维数组:
uint8_t blocks[16][16][16]
问题是我找不到将我的 int 数组发送到着色器的方法。我尝试使用 VBO,但它没有任何意义(我没有得到任何结果)。我还尝试使用 glUniform1iv() 发送我的数组,但我失败了。
- 是否可以使用 glUniformX() 将 int 数组发送到着色器?
- 为了防止存储大数据,我可以用 glUniformX() 设置一个字节 (uint8_t) 而不是 int 吗?
- 有什么好的方法可以将这么多数据发送到我的着色器吗?
- 实例化绘制是用不同 textures/types 个方块绘制同一模型的好方法。
出于所有目的和意图,应将此类数据视为纹理数据。这并不意味着字面上将其作为纹理数据上传,而是在考虑如何传输它时应该使用的思维框架。
或者,用更基本的术语来说:不要尝试将此数据作为统一数据传递。
如果您可以访问 OpenGL 4.3+(对于大多数不超过 6-8 年的硬件来说,这是 合理安全的选择),那么着色器存储缓冲区将是最简洁的解决方案:
//GLSL:
layout(std430, binding = 0) buffer terrainData
{
int data[16][16][16];
};
void main() {
int terrainType = data[voxel.x][voxel.y][voxel.z];
//Do whatever
}
//HOST:
struct terrain_data {
int data[16][16][16];
};
//....
terrain_data data = get_terrain_data();
GLuint ssbo;
GLuint binding = 0;//Should be equal to the binding specified in the shader code
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, GLsizeiptr size, data.data, GLenum usage);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
在此之后您需要更新数据的任何点,只需绑定 ssbo
,调用 glBufferData
(或您更新缓冲区数据的首选方法),然后就可以了.
如果您受限于较旧的硬件,您确实有一些选择,但它们很快就会变得笨拙:
- 您可以使用统一缓冲区,它的行为与着色器存储缓冲区非常相似,但是
- 存储空间有限space(大多数实现中为 65kb)
- 有其他可能与您的用例相关或不相关的限制
- 您可以直接使用纹理,将地形数据转换为浮点值(或作为整数使用,如果硬件内部支持整数格式),然后在着色器中转换回
- 与几乎所有硬件兼容
- 但在着色器代码中需要额外的复杂性和计算
我支持 中列出的方法,但提出了一个略有不同的建议。由于您的原始数据类型只是 uint8_t
,直接使用 SSBO 或 UBO 将需要每个元素浪费 3 个字节或手动将 4 个元素打包成一个 uint
。来自@Xirema 的回答:
For all purposes and intents, data of this type should be treated like texture data. This doesn't mean literally uploading it as texture data, but rather that that's the frame of thinking you should be using when considering how to transfer it.
我完全同意。因此我推荐使用 Texture Buffer Object (TBO), (a.k.a. "Buffer Texture")。
使用 glTexBuffer()
您基本上可以将缓冲区对象重新解释为纹理。在您的情况下,您可以将 uint8_t[16][16][16]
数组打包到缓冲区中并将其解释为 GL_R8UI
"texture" 格式,如下所示:
//GLSL:
uniform usamplerBuffer terrainData;
void main() {
uint terrainType = texelFetch(terrainData, voxel.z * (16*16) + voxel.y * 16 + voxel.x).r
//Do whatever
}
//HOST:
struct terrain_data {
uint8_t data[16][16][16];
};
//....
terrain_data data = get_terrain_data();
GLuint tbo;
GLuint tex;
glGenBuffers(1, &tbo);
glBindBuffer(GL_TEXTURE_BUFFER, tbo);
glBufferData(GL_TEXTURE_BUFFER, sizeof(terrain_data), data.data, usage);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_BUFFER, tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8UI, tbo);
请注意,这 不会 将数据复制到某个纹理对象。访问纹理意味着直接访问缓冲区的内存。
TBO 的另一个优点是自 OpenGL 3.1 起可用。
我正在制作一个体素引擎,我可以渲染一个块。我正在使用实例化渲染,这意味着我可以通过一次绘制调用来渲染所有块。区块的每个方块都有一个单独的整数(从 0 到 4095),它定义了他的方块类型(0 代表空气,1 代表泥土,等等)。我希望能够通过在我的片段着色器中应用良好的纹理来渲染我的块。我的块包含一个三维数组:
uint8_t blocks[16][16][16]
问题是我找不到将我的 int 数组发送到着色器的方法。我尝试使用 VBO,但它没有任何意义(我没有得到任何结果)。我还尝试使用 glUniform1iv() 发送我的数组,但我失败了。
- 是否可以使用 glUniformX() 将 int 数组发送到着色器?
- 为了防止存储大数据,我可以用 glUniformX() 设置一个字节 (uint8_t) 而不是 int 吗?
- 有什么好的方法可以将这么多数据发送到我的着色器吗?
- 实例化绘制是用不同 textures/types 个方块绘制同一模型的好方法。
出于所有目的和意图,应将此类数据视为纹理数据。这并不意味着字面上将其作为纹理数据上传,而是在考虑如何传输它时应该使用的思维框架。
或者,用更基本的术语来说:不要尝试将此数据作为统一数据传递。
如果您可以访问 OpenGL 4.3+(对于大多数不超过 6-8 年的硬件来说,这是 合理安全的选择),那么着色器存储缓冲区将是最简洁的解决方案:
//GLSL:
layout(std430, binding = 0) buffer terrainData
{
int data[16][16][16];
};
void main() {
int terrainType = data[voxel.x][voxel.y][voxel.z];
//Do whatever
}
//HOST:
struct terrain_data {
int data[16][16][16];
};
//....
terrain_data data = get_terrain_data();
GLuint ssbo;
GLuint binding = 0;//Should be equal to the binding specified in the shader code
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, GLsizeiptr size, data.data, GLenum usage);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
在此之后您需要更新数据的任何点,只需绑定 ssbo
,调用 glBufferData
(或您更新缓冲区数据的首选方法),然后就可以了.
如果您受限于较旧的硬件,您确实有一些选择,但它们很快就会变得笨拙:
- 您可以使用统一缓冲区,它的行为与着色器存储缓冲区非常相似,但是
- 存储空间有限space(大多数实现中为 65kb)
- 有其他可能与您的用例相关或不相关的限制
- 您可以直接使用纹理,将地形数据转换为浮点值(或作为整数使用,如果硬件内部支持整数格式),然后在着色器中转换回
- 与几乎所有硬件兼容
- 但在着色器代码中需要额外的复杂性和计算
我支持 uint8_t
,直接使用 SSBO 或 UBO 将需要每个元素浪费 3 个字节或手动将 4 个元素打包成一个 uint
。来自@Xirema 的回答:
For all purposes and intents, data of this type should be treated like texture data. This doesn't mean literally uploading it as texture data, but rather that that's the frame of thinking you should be using when considering how to transfer it.
我完全同意。因此我推荐使用 Texture Buffer Object (TBO), (a.k.a. "Buffer Texture")。
使用 glTexBuffer()
您基本上可以将缓冲区对象重新解释为纹理。在您的情况下,您可以将 uint8_t[16][16][16]
数组打包到缓冲区中并将其解释为 GL_R8UI
"texture" 格式,如下所示:
//GLSL:
uniform usamplerBuffer terrainData;
void main() {
uint terrainType = texelFetch(terrainData, voxel.z * (16*16) + voxel.y * 16 + voxel.x).r
//Do whatever
}
//HOST:
struct terrain_data {
uint8_t data[16][16][16];
};
//....
terrain_data data = get_terrain_data();
GLuint tbo;
GLuint tex;
glGenBuffers(1, &tbo);
glBindBuffer(GL_TEXTURE_BUFFER, tbo);
glBufferData(GL_TEXTURE_BUFFER, sizeof(terrain_data), data.data, usage);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_BUFFER, tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8UI, tbo);
请注意,这 不会 将数据复制到某个纹理对象。访问纹理意味着直接访问缓冲区的内存。
TBO 的另一个优点是自 OpenGL 3.1 起可用。