为什么这个使用texelFetch的OpenGL代码不起作用?

7rfyedvj  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(195)

我编写了以下代码来渲染正方形瓦片的二维Map:


# define TILE_NUM_INDICES 6

inline static u32 GetRandomIntBetween(u32 min, u32 max) {
    return (u32)rand() % (max - min + 1) + min;
}

static void GetRandomTileMap(u32* map, u32 size) {
    for (int i = 0; i < size; i++) {
        u32 r = GetRandomIntBetween(0, 23);
        map[i] = r;
    }
}
NewRenderer::NewRenderer(const NewRendererInitialisationInfo& info)
    :m_tileShader("shaders\\TilemapVert2.glsl", "shaders\\TilemapFrag2.glsl"),
    m_worldMapSize(info.tilemapSizeX, info.tilemapSizeY),
    m_tilemapChunkSize(info.chunkSizeX, info.chunkSizeY),
    m_windowWidth(info.windowWidth),
    m_windowHeight(info.windowHeight)
{
    using namespace std;
    const u32 mapsize = info.tilemapSizeX * info.tilemapSizeY;
    m_worldTextureBytes = make_unique<u32[]>(mapsize);
    GetRandomTileMap(m_worldTextureBytes.get(), mapsize);
    glGenTextures(1, &m_worldTextureHandle);
    glBindTexture(GL_TEXTURE_2D, m_worldTextureHandle);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // GL_NEAREST is the better filtering option for this game
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, info.tilemapSizeX, info.tilemapSizeY, 0, GL_RED, GL_UNSIGNED_INT, m_worldTextureBytes.get());
    glGenerateMipmap(GL_TEXTURE_2D);

    glGenVertexArrays(1, &m_vao);
}

void NewRenderer::DrawChunk(
    const glm::ivec2& chunkWorldMapOffset,
    const glm::vec2& pos,
    const glm::vec2& scale,
    float rotation,
    ArrayTexture2DHandle texArray,
    const Camera2D& cam
) const
{
    m_tileShader.use();
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, glm::vec3(pos, 0.0f));
    model = glm::rotate(model, glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f));
    model = glm::scale(model, glm::vec3(scale, 1.0f));

    m_tileShader.setMat4("vpMatrix", cam.GetProjectionMatrix(m_windowWidth, m_windowHeight));
    m_tileShader.setMat4("modelMatrix", model);
    m_tileShader.SetIVec2("chunkOffset", chunkWorldMapOffset);
    m_tileShader.SetIVec2("chunkSize", m_tilemapChunkSize);
    m_tileShader.setInt("masterTileTexture", 0);
    m_tileShader.setInt("atlasSampler", 1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_worldTextureHandle);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);

    glBindVertexArray(m_vao);
    glDrawArrays(GL_TRIANGLES, 0, m_tilemapChunkSize.x * m_tilemapChunkSize.y * TILE_NUM_INDICES);

}

(顶点着色器)


# version 440 core

/*
cpp setup:
create a big index buffer

* /

layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 uv;
out vec3 TexCoords;

uniform mat4 vpMatrix;
uniform mat4 modelMatrix;
uniform ivec2 chunkOffset;
uniform ivec2 chunkSize;
uniform sampler2D masterTileTexture;

# define TILE_NUM_VERTS 4

# define NUM_TILE_INDICES 6

void main()
{

    // vertices and indices that make up two triangles (a quad)
    // ie one tile in the map
    vec4 vertices[TILE_NUM_VERTS] = vec4[TILE_NUM_VERTS](
        vec4(0.5f,  0.5f,   1.0f, 1.0f),
        vec4(0.5f, -0.5f,   1.0f, 0.0f),
        vec4(-0.5f, -0.5f,  0.0f, 0.0f),
        vec4(-0.5f,  0.5f,   0.0f, 1.0f)
    );
    int indices[NUM_TILE_INDICES] = int[NUM_TILE_INDICES](
        0, 1, 3,   // first triangle
        1, 2, 3    // second triangle
    );

    // cycle through indicies
    int index = indices[int(gl_VertexID % NUM_TILE_INDICES)];

    // get base vertex
    vec4 baseVertex = vertices[index];

    // which tile in the map is being drawn?
    int whichTile = gl_VertexID / NUM_TILE_INDICES;

    // transfrom into x y coords of tile in the chunk
    ivec2 tilexy = ivec2(int(whichTile / chunkSize.y), int(whichTile % chunkSize.y));

    // translate base vertex by tilexy
    baseVertex.xy += vec2(tilexy);

    // set the z coord of the tex coords passed based on what tile is here
    // in the master tile map.

    // based on shader output all steps up to here are successful, a grid is drawn.
    // The problem is the texelFetch is not working, it's always the same tile drawn.
    TexCoords = vec3(
        baseVertex.zw,
        // changing this to different hard coded values does change what tile is drawn as expectd so sampler2DArray is setup correctly
        float(texelFetch(masterTileTexture, tilexy + chunkOffset, 0).r)); 

    gl_Position = vpMatrix * modelMatrix * vec4(baseVertex.xy, 0.0, 1.0);

}

(Frag着色器)


# version 440 core

uniform sampler2DArray atlasSampler;
in vec3 TexCoords;
out vec4 FragColor;

void main()
{
    FragColor = texture(atlasSampler, TexCoords);
}

这个想法是用来绘制一个大纹理的块,其中的每个像素代表一个瓷砖。基本的前提似乎是工作,瓷砖网格被绘制,但是顶点着色器中的texelFetch线似乎不工作,或者包含瓷砖索引的纹理没有正确设置,因为它只绘制索引为0的瓷砖。
为了测试它,我试着制作一个包含tile索引纹理的随机值的纹理,调试代码时我可以看到随机值被插入到了纹理缓冲区中。
我以前在着色器中使用过texelFetch,它工作正常,就我所知,我使用它是正确的。
任何人都能发现我的代码有什么问题吗?

sbtkgmzw

sbtkgmzw1#

glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, info.tilemapSizeX, info.tilemapSizeY, 0, GL_RED, GL_UNSIGNED_INT, m_worldTextureBytes.get());

这将创建一个规格化定点格式的纹理。当您在着色器中读取它时(通过texelFetch),该值将始终介于0和1之间,因此将从数组纹理中对第0层进行采样。
OpenGL 4.4支持整数纹理格式,这是您应该在此处使用的格式。请将第一个GL_RED替换为GL_R8UIGL_16UIGL_R32UI,以更合适的为准,并将第二个GL_RED替换为GL_RED_INTEGER。例如:

glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, //<---
    info.tilemapSizeX, info.tilemapSizeY, 0, GL_RED_INTEGER, //<---
    GL_UNSIGNED_INT, m_worldTextureBytes.get());

此外,您必须将着色器中的sampler2D更改为匹配的整数采样器类型。对于上述内部格式,匹配的采样器将为usampler2D

uniform usampler2D masterTileTexture;

**EDIT:**此外,您还必须将MAG过滤器设置为GL_NEAREST,因为它是唯一受支持的过滤器:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

(MIN滤波器也可以是GL_NEAREST_MIPMAP_NEAREST。)

相关问题