【问题标题】:Using Texture Atlas as Texture Array in OpenGL在 OpenGL 中使用纹理图集作为纹理数组
【发布时间】:2020-04-03 05:52:06
【问题描述】:

我现在正在构建一个体素游戏。一开始,我使用存储所有体素纹理的纹理图集,它工作正常。之后,我决定在我的游戏中使用 Greedy Meshing,因此纹理图集不再有用。我读了一些文章说应该使用纹理数组。然后我尝试阅读并使用纹理数组技术进行纹理处理。然而,我得到的结果在我的游戏中是全黑的。那我错过了什么?

这是我的纹理图集 (600 x 600)

my texture atlas

这是我的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;
}  

这是我的渲染结果

rendering result

编辑 1:

我发现纹理图集不适用于纹理数组,因为它是一个网格,因此 OpenGL 无法决定它应该从哪里开始。所以我创建了一个垂直纹理(18 x 72)并再次尝试,但它仍然到处都是黑色。

my verticle texture atlas

我在使用之前检查了绑定纹理。

【问题讨论】:

    标签: c++ opengl texture-mapping opengl-3 voxel


    【解决方案1】:

    当指定 3 维纹理图像时,深度必须是必须存储在数组中的图像数量(例如 imageCount)。 width 和 height 参数表示 1 个图块的宽度和高度(例如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]);
    }
    

    注意,您必须从纹理图集中提取图块并在纹理数组中设置每个子图像。 (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());
        }
    }
    

    【讨论】:

    • 哦,它确实有效!!!!你真的救了我的命,我已经被这个问题困了一个星期。非常感谢!!!!
    • 我相信在提取瓦片的算法中,ptr 应该是data + iy*rowLen*tileH + ix*tileSizeX;(乘以tileH),否则复制的开始将被像素行偏移,而不是瓦片行。 beforeafter.
    • @Minebomber rowLen = tilesX * tileSizeXtileSizeX = tileW * channels
    猜你喜欢
    • 2014-12-30
    • 2018-03-31
    • 2013-03-28
    • 2016-04-09
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 2021-01-15
    相关资源
    最近更新 更多