Несколько текстур кубической карты ведут себя странно, когда количество текстур увеличивается - PullRequest
0 голосов
/ 03 мая 2020

Итак, мне удалось успешно реализовать пакетную обработку в моем движке, но я столкнулся с некоторым странным поведением с массивом samplerCubes в моем фрагментном шейдере. Пакетное средство рендеринга отлично работает с 2-мя текстурными единицами - я могу успешно рисовать кубы с правильными текстурами, если я связываю только 2 текстуры, но как только я добавляю 3-ю к вектору mTextures и использую glUniform1i для этого индекса в В массиве samplerCube отображается неправильная текстура, хотя идентификатор текстуры (TexID) правильный (я проверял это в фрагментном шейдере).

Проблема, как мне кажется, связана с моим пониманием OpenGL, поскольку по какой-то причине текстура, отображаемая с помощью textureCubes[2] (унифицированный фрагментный шейдер), отображается с помощью textureCubes[1], а textureCubes[2] отображает то же самое текстура как textureCubes[0]. То, что должно отображаться textureCubes[1], просто не существует с 3-й границей текстуры.

Вот мой код:

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);
}

Ответы [ 2 ]

0 голосов
/ 04 мая 2020

Для тех, кто ищет решение, мне потребовалось некоторое время, но я нашел его (я относительно новичок в OpenGL). Проблема была в том, что я настраивал слоты сэмплера текстуры по отдельности, используя glUniform1i. Чтобы исправить это, поскольку cubeTextures является массивом samplerCubes, я создал структуру для данных сэмплера, содержащую массив int и длину, и установил слоты сэмплера текстуры, используя glUniform1iv.

0 голосов
/ 03 мая 2020

[...] textureCubes[1], а textureCubes[2] отображает ту же текстуру, что и textureCubes[0].

Поведение шейдера фрагмента не определено из-за:

in flat float TexID;
uniform samplerCube cubeTextures[3];
int id = int(TexID);
... cubeTextures[id] ...

cubeTextures - это массив сэмплеров карты куба, а TexID - это входной фрагментный шейдер, поэтому TexID не является Динамически однородным выражением

См. GLSL версии 4.60 (самая последняя) (из OpenGL Shading Language 4.60 Спецификация - 4.1.7. Непрозрачные типы ):

При агрегировании в массивы внутри шейдера, эти типы могут быть проиндексированы только с динамически однородным выражением, иначе поиск текстуры приведет к неопределенным значениям.


Я рекомендую использовать samplerCubeArray (см. Sampler ) вместо массива samplerCube.
Когда вы используете samplerCubeArray, тогда вам вообще не нужно индексирование, потому что «индекс» кодируется в 4-м компоненте координаты текстуры при поиске текстуры (см. texture).

...