在 OpenGL 中使用纹理图集作为纹理数组
Using Texture Atlas as Texture Array in OpenGL
我正在构建一个 Voxel 游戏。一开始,我使用存储所有体素纹理的纹理图集,它工作正常。在那之后,我决定在我的游戏中使用 Greedy Meshing,因此纹理图集不再有用。我读了一些文章说应该改用纹理数组。然后我尝试阅读并使用纹理数组技术进行纹理化。但是,我得到的结果在我的游戏中是全黑的。那我错过了什么?
这是我的纹理图集 (600 x 600)
这是我的 Texture2DArray,我用这个 class 来读取和保存纹理数组
Texture2DArray::Texture2DArray() : Internal_Format(GL_RGBA8), Image_Format(GL_RGBA), Wrap_S(GL_REPEAT), Wrap_T(GL_REPEAT), Wrap_R(GL_REPEAT), Filter_Min(GL_NEAREST), Filter_Max(GL_NEAREST), Width(0), Height(0)
{
glGenTextures(1, &this->ID);
}
void Texture2DArray::Generate(GLuint width, GLuint height, unsigned char* data)
{
this->Width = width;
this->Height = height;
glBindTexture(GL_TEXTURE_2D_ARRAY, this->ID);
// I cannot decide what the texture array layer (depth) should be (I put here is 1 for layer number)
//Can anyone explain to me how to decide the texture layer here?
glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, this->Internal_Format, this->Width, this->Height, 0, 1 , this->Image_Format, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, this->Wrap_S);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, this->Wrap_T);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, this->Wrap_R);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, this->Filter_Min);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, this->Filter_Max);
//unbind this texture for another creating texture
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
}
void Texture2DArray::Bind() const
{
glBindTexture(GL_TEXTURE_2D_ARRAY, this->ID);
}
这是我的片段着色器
#version 330 core
uniform sampler2DArray ourTexture;
in vec2 texCoord;
out vec4 FragColor;
void main(){
// 1 (the layer number) just for testing
FragColor = texture(ourTexture,vec3(texCoord, 1));
}
这是我的顶点着色器
#version 330 core
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inTexCoord;
out vec2 texCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
gl_Position = projection * view * vec4(inPos,1.0f);
texCoord = inTexCoord;
}
这是我的渲染结果
编辑 1:
我发现纹理图集不适用于纹理数组,因为它是一个网格,所以 OpenGl 无法决定它应该从哪里开始。所以我创建了一个垂直纹理 (18 x 72) 并重试,但它仍然到处都是黑色。
我在使用之前检查了绑定纹理。
当指定3维纹理图像时,深度必须是必须存储在数组中的图像数量(例如imageCount
)。宽度和高度参数表示 1 个图块的宽度和高度(例如 tileW
、tileH
)。图层应为 0,边框参数必须为 0。参见 glTexImage3D
。 glTexImage3D
为纹理图像创建数据存储。保留纹理所需的内存 (GPU)。可以传递指向图像数据的指针,但这不是必需的。
如果所有的tile都存储在一个垂直图集中,那么可以直接设置图像数据:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, this->Internal_Format,
tileW, tileH, imageCount, 0,
this->Image_Format, GL_UNSIGNED_BYTE, data);
如果图块在 16x16 图集中,则必须从纹理图集中提取图块并在纹理数组中设置每个子图像。 (data[i]
为一张图块的成像数据)。创建纹理图像:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, this->Internal_Format,
tileW, tileH, imageCount, 0,
this->Image_Format, GL_UNSIGNED_BYTE, nullptr);
之后使用glTexSubImage3D
将纹理数据放入纹理对象的数据存储中。 glTexSubImage3D
使用现有数据存储并复制数据。例如:
for (int i = 0; i < imageCount; ++i)
{
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
0, 0, i,
tileW, tileH, 1,
this->Image_Format, GL_UNSIGNED_BYTE, data[i]);
}
请注意,您必须从纹理图集中提取图块并在纹理数组中设置每个子图像。 (data[i]
为一张图块的成像数据)
提取图块并指定纹理图像的算法可能如下所示
#include <algorithm> // std::copy
#include <vector> // std::vector
unsigned char* data = ...; // 16x16 texture atlas image data
int tileW = ...; // number of pixels in a row of 1 tile
int tileH = ...; // number of pixels in a column of 1 tile
int channels = 4; // 4 for RGBA
int tilesX = 16;
int tilesY = 16;
int imageCount = tilesX * tilesY;
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, this->Internal_Format,
tileW, tileH, imageCount, 0,
this->Image_Format, GL_UNSIGNED_BYTE, nullptr);
std::vector<unsigned char> tile(tileW * tileH * channels);
int tileSizeX = tileW * channels;
int rowLen = tilesX * tileSizeX;
for (int iy = 0; iy < tilesY; ++ iy)
{
for (int ix = 0; ix < tilesX; ++ ix)
{
unsigned char *ptr = data + iy*rowLen + ix*tileSizeX;
for (int row = 0; row < tileH; ++ row)
std::copy(ptr + row*rowLen, ptr + row*rowLen + tileSizeX,
tile.begin() + row*tileSizeX);
int i = iy * tilesX + ix;
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
0, 0, i,
tileW, tileH, 1,
this->Image_Format, GL_UNSIGNED_BYTE, tile.data());
}
}
我正在构建一个 Voxel 游戏。一开始,我使用存储所有体素纹理的纹理图集,它工作正常。在那之后,我决定在我的游戏中使用 Greedy Meshing,因此纹理图集不再有用。我读了一些文章说应该改用纹理数组。然后我尝试阅读并使用纹理数组技术进行纹理化。但是,我得到的结果在我的游戏中是全黑的。那我错过了什么?
这是我的纹理图集 (600 x 600)
这是我的 Texture2DArray,我用这个 class 来读取和保存纹理数组
Texture2DArray::Texture2DArray() : Internal_Format(GL_RGBA8), Image_Format(GL_RGBA), Wrap_S(GL_REPEAT), Wrap_T(GL_REPEAT), Wrap_R(GL_REPEAT), Filter_Min(GL_NEAREST), Filter_Max(GL_NEAREST), Width(0), Height(0)
{
glGenTextures(1, &this->ID);
}
void Texture2DArray::Generate(GLuint width, GLuint height, unsigned char* data)
{
this->Width = width;
this->Height = height;
glBindTexture(GL_TEXTURE_2D_ARRAY, this->ID);
// I cannot decide what the texture array layer (depth) should be (I put here is 1 for layer number)
//Can anyone explain to me how to decide the texture layer here?
glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, this->Internal_Format, this->Width, this->Height, 0, 1 , this->Image_Format, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, this->Wrap_S);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, this->Wrap_T);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, this->Wrap_R);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, this->Filter_Min);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, this->Filter_Max);
//unbind this texture for another creating texture
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
}
void Texture2DArray::Bind() const
{
glBindTexture(GL_TEXTURE_2D_ARRAY, this->ID);
}
这是我的片段着色器
#version 330 core
uniform sampler2DArray ourTexture;
in vec2 texCoord;
out vec4 FragColor;
void main(){
// 1 (the layer number) just for testing
FragColor = texture(ourTexture,vec3(texCoord, 1));
}
这是我的顶点着色器
#version 330 core
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inTexCoord;
out vec2 texCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
gl_Position = projection * view * vec4(inPos,1.0f);
texCoord = inTexCoord;
}
这是我的渲染结果
编辑 1:
我发现纹理图集不适用于纹理数组,因为它是一个网格,所以 OpenGl 无法决定它应该从哪里开始。所以我创建了一个垂直纹理 (18 x 72) 并重试,但它仍然到处都是黑色。
我在使用之前检查了绑定纹理。
当指定3维纹理图像时,深度必须是必须存储在数组中的图像数量(例如imageCount
)。宽度和高度参数表示 1 个图块的宽度和高度(例如 tileW
、tileH
)。图层应为 0,边框参数必须为 0。参见 glTexImage3D
。 glTexImage3D
为纹理图像创建数据存储。保留纹理所需的内存 (GPU)。可以传递指向图像数据的指针,但这不是必需的。
如果所有的tile都存储在一个垂直图集中,那么可以直接设置图像数据:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, this->Internal_Format,
tileW, tileH, imageCount, 0,
this->Image_Format, GL_UNSIGNED_BYTE, data);
如果图块在 16x16 图集中,则必须从纹理图集中提取图块并在纹理数组中设置每个子图像。 (data[i]
为一张图块的成像数据)。创建纹理图像:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, this->Internal_Format,
tileW, tileH, imageCount, 0,
this->Image_Format, GL_UNSIGNED_BYTE, nullptr);
之后使用glTexSubImage3D
将纹理数据放入纹理对象的数据存储中。 glTexSubImage3D
使用现有数据存储并复制数据。例如:
for (int i = 0; i < imageCount; ++i)
{
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
0, 0, i,
tileW, tileH, 1,
this->Image_Format, GL_UNSIGNED_BYTE, data[i]);
}
请注意,您必须从纹理图集中提取图块并在纹理数组中设置每个子图像。 (data[i]
为一张图块的成像数据)
提取图块并指定纹理图像的算法可能如下所示
#include <algorithm> // std::copy
#include <vector> // std::vector
unsigned char* data = ...; // 16x16 texture atlas image data
int tileW = ...; // number of pixels in a row of 1 tile
int tileH = ...; // number of pixels in a column of 1 tile
int channels = 4; // 4 for RGBA
int tilesX = 16;
int tilesY = 16;
int imageCount = tilesX * tilesY;
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, this->Internal_Format,
tileW, tileH, imageCount, 0,
this->Image_Format, GL_UNSIGNED_BYTE, nullptr);
std::vector<unsigned char> tile(tileW * tileH * channels);
int tileSizeX = tileW * channels;
int rowLen = tilesX * tileSizeX;
for (int iy = 0; iy < tilesY; ++ iy)
{
for (int ix = 0; ix < tilesX; ++ ix)
{
unsigned char *ptr = data + iy*rowLen + ix*tileSizeX;
for (int row = 0; row < tileH; ++ row)
std::copy(ptr + row*rowLen, ptr + row*rowLen + tileSizeX,
tile.begin() + row*tileSizeX);
int i = iy * tilesX + ix;
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
0, 0, i,
tileW, tileH, 1,
this->Image_Format, GL_UNSIGNED_BYTE, tile.data());
}
}