当纹理数量增加时,多个立方体贴图纹理表现异常

Multiple cube map textures behaving strangely when amount of textures increases

所以我已经成功地在我的引擎中实现了批处理,但是在我的片段着色器中遇到了 samplerCubes 数组的一些奇怪行为。批处理渲染器可以很好地处理 2 个纹理单元 - 如果我只绑定 2 个纹理,我可以成功地绘制具有正确纹理的立方体,但是一旦我将第 3 个添加到 mTextures 向量,并使用 glUniform1i对于 samplerCube 数组中的那个索引,显示了错误的纹理,即使纹理 ID (TexID) 是正确的(我在片段着色器中检查过)。

问题似乎出在我对 OpenGL 的理解上,由于某种原因,应该由 textureCubes[2] 显示的纹理(片段着色器统一)由 textureCubes[1] 显示,[=20] =] 显示与 textureCubes[0] 相同的纹理。 textureCubes[1] 应显示的内容在第三个纹理绑定中不存在。

这是我的代码:

Main.cpp

#include "Window.h"
#include "Block.h"
#include "BatchRenderer.h"
#include "Shader.h"
#include "Camera.h"

void Submit(gfx::BatchRenderer* renderer, float height) //temporary to test batching
{  
    for (int i = 0; i < 16; i++)
    {
        for (int j = 0; j < 16; j++)
        {
            gfx::Block* block = new gfx::Block(j % 2 == 0 ? (i % 2 == 0 ? gfx::BlockType::DIRT : gfx::BlockType::GRASS) : gfx::BlockType::COBBLE, math::Vec3f(i * 5.0, height, j * 5.0));
            renderer->Submit(block);
        }
    }
}

int main()
{
    gfx::Window* window = new gfx::Window("MineClone", 800, 800);

    gfx::Shader* shader = new gfx::Shader();
    shader->FromFile(GL_VERTEX_SHADER, "Resources/ModelTest.vert");
    shader->FromFile(GL_FRAGMENT_SHADER, "Resources/ModelTest.frag");
    shader->RefreshProgram();

    math::Mat4f projection = math::Mat4f::Perspective(70.0, window->GetWidth() / window->GetHeight(), 0.1, 1000.0);
    gfx::Camera* camera = new gfx::Camera(projection, 0.05, 0.0015);

    gfx::BatchRenderer* renderer = new gfx::BatchRenderer();

    gfx::TextureCube* grass = new gfx::TextureCube("Resources/top.png", "Resources/dirt.png", "Resources/sides.png");
    renderer->Submit(grass);

    gfx::TextureCube* dirt = new gfx::TextureCube("Resources/dirt.png");
    renderer->Submit(dirt);

    gfx::TextureCube* cobble = new gfx::TextureCube("Resources/cobble.png");
    renderer->Submit(cobble);

    Submit(renderer, 0.0);
    Submit(renderer, 5.0);
    Submit(renderer, 10.0);
    Submit(renderer, 15.0);
    Submit(renderer, 20.0);
    Submit(renderer, 25.0);
    Submit(renderer, 30.0);
    Submit(renderer, 35.0);

    while (!window->IsClosed())
    {
        window->Update();
        if (window->GetInputHandler()->IsKeyDown(VK_ESCAPE))
            window->SwitchMouseState();

        if (window->IsSynced())
            camera->Update(window->GetInputHandler());
    
        shader->Bind();
        math::Mat4f projection = camera->GetProjection();
        shader->SetUniform("projection", projection);

        math::Mat4f view = camera->GetView();
        shader->SetUniform("view", view);

        shader->SetUniform("cubeTextures[0]", 0);
        shader->SetUniform("cubeTextures[1]", 1);
        shader->SetUniform("cubeTextures[2]", 2);

        renderer->Render();
        shader->Unbind();
    }

    return 0;
}

BatchRenderer.cpp

#include "BatchRenderer.h"

namespace gfx
{
    BatchRenderer::BatchRenderer()
        : mMesh(NULL)
    {
    }

    BatchRenderer::~BatchRenderer()
    {
        mBlocks.clear();
        mTextures.clear();
        delete mMesh;
    }

    void BatchRenderer::Submit(Block* block)
    {
        MeshData data = block->GetMesh();
        mMeshData.vertices.insert(mMeshData.vertices.end(), 
            data.vertices.begin(), 
            data.vertices.end());

        for (int i = 0; i < 36; i++)
        {
            data.indices[i] += mBlocks.size() * 8;
        }
        mMeshData.indices.insert(mMeshData.indices.end(),
            data.indices.begin(),
            data.indices.end());

        mMesh = Mesh::Make(mMeshData);
        mBlocks.push_back(block);
    }

    void BatchRenderer::Submit(TextureCube* texture)
    {
        mTextures.push_back(texture);
    }

    void BatchRenderer::Render()
    {
        for (int i = 0; i < mTextures.size(); i++)
        {
            mTextures[i]->Bind(i);
        }

        mMesh->Render();

        for (int i = 0; i < mTextures.size(); i++)
        {
            mTextures[i]->Unbind(i);
        }
    }
}

TextureCube.cpp

#include "TextureCube.h"

namespace gfx
{
    TextureCube::TextureCube(std::string paths[6])
        : Texture(TextureEnum::TEXTURE_CUBE)
    {
        glGenTextures(1, &mHandle);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);

        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        for (int i = 0; i < 6; i++)
        {
            std::vector<byte> pixels;
            lodepng::decode(pixels, mWidth, mHeight, paths[i].c_str());
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
        }

        glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    }

    TextureCube::TextureCube(std::string path)
        : Texture(TextureEnum::TEXTURE_CUBE)
    {
        glGenTextures(1, &mHandle);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);

        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        std::vector<byte> pixels;
        lodepng::decode(pixels, mWidth, mHeight, path.c_str());
        for (int i = 0; i < 6; i++)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
        }

        glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    }

    TextureCube::TextureCube(std::string top, std::string bottom, std::string sides)
        : Texture(TextureEnum::TEXTURE_CUBE)
    {
        glGenTextures(1, &mHandle);
        glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);

        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        std::vector<byte> topPixels;
        lodepng::decode(topPixels, mWidth, mHeight, top.c_str());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, topPixels.data());

        std::vector<byte> bottomPixels;
        lodepng::decode(bottomPixels, mWidth, mHeight, bottom.c_str());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bottomPixels.data());

        std::vector<byte> sidesPixels;
        lodepng::decode(sidesPixels, mWidth, mHeight, sides.c_str());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());

        glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    }

    TextureCube::~TextureCube()
    {
        glDeleteTextures(1, &mHandle);
        mHandle = NULL;
    }

    void TextureCube::Bind(uint32_t slot)
    {
        glBindTextureUnit(slot, mHandle);
    }

    void TextureCube::Unbind(uint32_t slot)
    {
        glBindTextureUnit(slot, 0);
    }
}

ModelTest.vert

#version 450 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec3 offset;
layout (location = 3) in float textureID;

uniform mat4 projection;
uniform mat4 view;

out vec3 TexPos;
out flat float TexID;

void main()
{
    gl_Position = projection * view * vec4(position, 1.0);
    TexPos = position - offset;
    TexID = textureID;
}

ModelTest.frag

#version 450 core

out vec4 FragColour;

in vec3 TexPos;
in flat float TexID;
uniform samplerCube cubeTextures[3];

void main()
{
    vec3 norm = normalize(TexPos);
    int id = int(TexID);
    FragColour = texture(cubeTextures[id], norm);
}

对于寻找解决方案的任何人,我花了一段时间才找到它(我对 OpenGL 比较陌生)。问题是我使用 glUniform1i 单独设置纹理采样器槽。为了解决这个问题,因为 cubeTextures 是一个 samplerCubes 数组,我为包含一个 int 数组和一个长度的采样器数据创建了一个结构,并使用 glUniform1iv.

设置纹理采样器插槽