Search code examples
c++openglglsl

OpenGL sampler2D array


I have an array of sampler2D that looks like so: uniform sampler2D u_Textures[2];. I want to be able to render more textures(in this case 2) in the same drawcall. In my fragment shader, if I set the color output value to somethiung like red, it does show me 2 red squares witch leads me to belive I did something wrong when binding the textures. My code:

Texture tex1("Texture/lin.png");
tex1.Bind(0);
Texture tex2("Texture/font8.png");
tex2.Bind(1);

auto loc = glGetUniformLocation(sh.getRendererID(), "u_Textures");
GLint samplers[2] = { 0, 1 };
glUniform1iv(loc, 2, samplers);
void Texture::Bind(unsigned int slot) const {
    glActiveTexture(slot + GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_RendererID);
}

And this is my fragment shader:

#version 450 core
layout(location = 0) out vec4 color;

in vec2 v_TexCoord;
in float v_texIndex;

uniform sampler2D u_Textures[2];

void main() 
{
    int index = int(v_texIndex);
    vec4 texColor = texture(u_Textures[index], v_TexCoord);
    if (texColor.a == 0.0) {
        // this line fills the a = 0.0f pixels with the color red  
        color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
    }
    else color = texColor;
}

Also, it only draws the tex2 texture on the screen. This is my verticie attributes:

float pos[24 * 2] = {
        // position xy z    texture coordinate, texture index
        -2.0f, -1.5f, 0.0f,     0.0f, 0.0f, 0.0f,
        -1.5f, -1.5f, 0.0f,     1.0f, 0.0f, 0.0f,
        -1.5f, -2.0f, 0.0f,     1.0f, 1.0f, 0.0f, // right side bottom
        -2.0f, -2.0f, 0.0f,     0.0f, 1.0f, 0.0f,

        0.5f, 0.5f, 0.0f,   0.0f, 0.0f, 1.0f,
        1.5f, 0.5f, 0.0f,   1.0f, 0.0f, 1.0f, 
        1.5f, 1.5f, 0.0f,   1.0f, 1.0f, 1.0f,
        0.5f, 1.5f, 0.0f,   0.0f, 1.0f, 1.0f
    };

No matter how I chenge the texture index, it only draws 1 of those 2 textures.


Solution

  • You cannot use a fragment shader input variable to index an array of texture samplers. You have to use a sampler2DArray (GL_TEXTURE_2D_ARRAY) instead of an array of sampler2D (GL_TEXTURE_2D).

    int index = int(v_texIndex);
    vec4 texColor = texture(u_Textures[index], v_TexCoord);
    

    is undefined behavior because v_texIndex is a fragment shader input variable and therefore not a dynamically uniform expression. See GLSL 4.60 Specification - 4.1.7. Opaque Types

    [...] Texture-combined sampler types are opaque types, [...]. When aggregated into arrays within a shader, they can only be indexed with a dynamically uniform integral expression, otherwise results are undefined.


    Example using sampler2DArray:

    #version 450 core
    layout(location = 0) out vec4 color;
    
    in vec2 v_TexCoord;
    in float v_texIndex;
    
    uniform sampler2DArray u_Textures;
    
    void main() 
    {
        color = texture(u_Textures, vec3(v_TexCoord.xy, v_texIndex));
    }
    

    texture is overloaded for all sampler types. The texture coordinates and texture layer need not to dynamically uniform, but the index into a sampler array has to be dynamically uniform.


    To be clear, the problem isn't with the array of sampler (the problem is not sampler2D u_Textures[2];). The problem is the indexing. The problem is that v_texIndex is not dynamically uniform (the problem is in float v_texIndex;). It works when the index is dynamically uniform (e.g. uniform float v_texIndex; will work). Also the specification just says that the result is undefined. So there may be some systems where it works.