在OpenGL中将纹理集用作纹理数组

5
我现在正在开发一个体素游戏。一开始,我使用了一个纹理图集来存储所有的体素纹理,它可以正常工作。之后,我决定在我的游戏中使用贪婪网格化技术,因此纹理图集就无用了。我阅读了一些文章,说应该使用纹理数组代替。然后我尝试使用纹理数组来进行贴图。但是,在我的游戏中得到的结果却全部是黑色的。那么我错过了什么吗?
这是我的纹理图集(600 x 600) 以下是我的Texture2DArray类,我使用这个类来读取和保存纹理数组。
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)并尝试了一次,但仍然全黑。

我已经检查过在使用纹理之前绑定纹理。

1个回答

9
当指定3D纹理图像时,深度必须是存储在数组中的图像数量(例如imageCount)。宽度和高度参数表示一个瓷砖的宽度和高度(例如tileWtileH)。 层数应为0,边框参数必须为0。请参见glTexImage3DglTexImage3D创建纹理图像的数据存储区。所需的纹理内存被保留(GPU)。可以传递指向图像数据的指针,但不是必需的。
如果所有瓷砖都存储在垂直图集中,则可以直接设置图像数据:
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]);
}

注意,您需要从纹理图集中提取tiles,并将每个子图像设置到纹理数组中。(data[i]是一个tile的图像数据)
提取tiles并指定纹理图像的算法可能如下所示。
#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());
    }
}

3
哦,它真的奏效了!!!你真的救了我的命,我被困在这个问题里已经有一周了。非常感谢你!!! - QDinh
1
我认为在提取瓦片的算法中,ptr 应该是 data + iy*rowLen*tileH + ix*tileSizeX;(乘以 tileH),否则复制的起始位置将会偏移像素行而不是瓦片行。之前之后 - Minebomber
@Minebomber rowLen = tilesX * tileSizeXtileSizeX = tileW * channels - Rabbid76

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接