Quick texture array question

Started by
11 comments, last by Misantes 9 years, 2 months ago

Generally, my method for creating/using texture arrays works fine. However, I've noticed that the use of arrays seemingly negates my ability to use any textures that are not part of the array, even if loaded separately. Specifically, I'm trying to access my depth texture for a shadow map. If I bind the diffuse/normal/specular maps individually, and not part of the array, things render as expected. If I bind using an array, the shadow map is not accessible.

I'm wondering if binding the array overrides the non-arrayed shadow map or if I should be calling things differently.

I'm likely making a small mistake here, but is there a way to use both in a shader? The opengl docs don't mention this scenario.

pseudo-code example:


//binding
//shadow map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glUniform1i(shadowMapID, 0);

//diffuse image
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(LayerNumID1, imageIndex.x);

//normal map
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(LayerNumID2, imageIndex.y);

//specular map
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(LayerNumID3, imageIndex.z);

in fragment shader:


uniform sampler2DShadow shadowMap;
uniform sampler2DArray DiffuseTextureSampler;
uniform sampler2DArray NormalTextureSampler;
uniform sampler2DArray SpecularTextureSampler;
uniform int layer_index1;
uniform int layer_index2;
uniform int layer_index3;

Beginner here <- please take any opinions with grain of salt

Advertisement

The general idea is certainly possible, buts its hard to tell from pseudo-code, youre binding "texturearray" to three different texture units, and using three different samplers in the shader? plus passing a 0 to the shader for the shadow uniform, not sure if you are actually doing that or this is just a mistake in the example code.

But in general you should be able to set active texture, bind, set active texture + 1, bind, etc.

Ugh, have I been wrong about that all along? I was under the impression that that value was for the texture unit. I.e. if it's GL_TEXTURE0, then you bind 0, if it's GL_TEXTURE1, then you bind 1, unless it's an array, then you bind the index location \.

I've looked at the opengl docs multiple times over this, but they seem...less than helpful in this instance. Though, perhaps I'm just not following the terminology correctly:


v0, v1, v2, v3
For the scalar commands, specifies the new values to be used for the specified uniform variable.

To your other comment. I've always just used the 3 samplers (my shaders are relatively simple) for diffuse/specular/normals. I gather from your comments it's possible to just bind one sampler array, and index it in the shader similarly as an array. I'll incorporate that. It definitely seems ideal.

Beginner here <- please take any opinions with grain of salt

As NumberXaero suggested, you bind the array to one location, say 1:


glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(location, 1);

In shaders you use


uniform sampler2DArray arrayTex;

/* ... */

void main()
{
 float index = 0.0;
 vec4 color = texture(arrayTex, vec3(texCoord.xy, index));
 /* ... */
}

The third component of the second argument to texture is the array index. Arrays are great for overcoming texture image unit limitations, although the layers will need to have the same properties

Ok, I get that, and other than using multiple id's in the shader, I'm using them correctly, I believe. But, to the other question, when binding a single texture, the second value for the glUniform1i(), what actually goes there, if not the texture number (again, say 0 for GL_TEXTURE0)?

Beginner here <- please take any opinions with grain of salt

Ok, I get that, and other than using multiple id's in the shader, I'm using them correctly, I believe. But, to the other question, when binding a single texture, the second value for the glUniform1i(), what actually goes there, if not the texture number (again, say 0 for GL_TEXTURE0)?

The value for glUniform1i() maps to the active texture unit, i.e., glUniform1i(textureUniformLoc, 4); corresponds to glActiveTexture(GL_TEXTURE4); This works the same for all types of textures. Only difference is how you access the texture data in the shaders. I usually set the uniform once during initialization when setting up the shaders.

If this is what youre doing, as jmakitalo posted

As NumberXaero suggested, you bind the array to one location, say 1:


glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(location, 1);

In shaders you use


uniform sampler2DArray arrayTex;

/* ... */

void main()
{
 float index = 0.0;
 vec4 color = texture(arrayTex, vec3(texCoord.xy, index));
 /* ... */
}

The third component of the second argument to texture is the array index. Arrays are great for overcoming texture image unit limitations, although the layers will need to have the same properties

then thats correct. You generate an id and it goes to bind, you set an activate texture unit before bind, and pass that active index to the sampler location. Assuming

"texturearray" isnt a variable being changed between the bind calls, and assuming you are using three arrays containing the textures grouped by use


//binding
//shadow map
glActiveTexture(GL_TEXTURE0);                  // unit 0
glBindTexture(GL_TEXTURE_2D, depthTexture);    // GenTexture id
glUniform1i(shadowMapSamplerUniformLocation, 0);     // sampler (index)

//diffuse image
glActiveTexture(GL_TEXTURE1);                               // unit 1
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearrayDiffuse);    // GenTexture id, bound to 1
glUniform1i(diffuseArraySamplerUniformLocation, 1);        // sampler (index)
glUniform1i(LayerNumID1, imageIndex.x);       // the diffuse you want to access I take it?

//normal image
glActiveTexture(GL_TEXTURE2);                               // unit 2
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearrayNormal);    // GenTexture id, bound to 2
glUniform1i(normalArraySamplerUniformLocation, 2);        // sampler (index)
glUniform1i(LayerNumID2, imageIndex.y);       // the normal you want to access I take it?

// spec......

if "texturearray" is a big list of diffuse/normal/spec textures altogether then the shader would have a single, uniform sampler2DArray, rather then 3, and setup


glActiveTexture(GL_TEXTURE1); // unit 1
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray); // GenTexture id, bound to 1, the only array
glUniform1i(arraySamplerUniformLocation, 1); // sampler (index)
glUniform1i(LayerNumID1, imageIndex.x); // the diffuse you want to access?
glUniform1i(LayerNumID2, imageIndex.y); // the normal you want to access?
glUniform1i(LayerNumID3, imageIndex.z); // the spec you want to access?

Short version, the sampler uniform needs the texture unit index.

In other words:

One has to make a distinction between the texture object name (the number generated by glGenTexture, and used as 2nd parameter to glBindTexture, for example), and the texture unit address (the trailing number at the symbolic constant when calling glActiveTexture or the 2nd parameter of glUniform1i in your case). The former names the texture, the latter the texture unit.

A texture unit is a part of the GPU hardware. From the outside, you bind a texture object to the texture unit, so saying "when this texture unit is accessed, then the returned data should come from this texture object"; that is done in 2 steps, first by glActiveTexture to specify the texture unit, then by glBindTexture to specify the texture object. Then, within your shader script, you have a sampler. The sampler works on a texture unit. So you need to call glUniform to specify which texture unit should be used by the specific sampler.

In the end you have an indirect access of the sampler onto the texture data, with the texture unit in-between:

texture object --bound_to--> texture unit <--read_from-- sampler

The second number to the call to glUniform1i is the texture unit the texture for the sampler is bound to. You cannot bind individual layers of an array texture to an sampler( just don't make any sense ). Also, I would recommend downloading the GL specification for the version you are using for reference. Its very handy to have whenever you need a little clarification.

Hm, I think due to a misunderstanding and posting pseudo-code, we're becoming a little off-base here. I think there was some initial confusion between us about binding 0 to GL_TEXTURE0, which I think has been cleared up. And, while I wasn't aware you could use a single sampler for the array, that seems relatively straightforward.

However, the initial problem still exists where I'm unable to use the array with the depth texture.

For clarity here are the bind calls and shader (after adjustments for the single sampler, thank you)


glActiveTexture(GL_TEXTURE0); 
glBindTexture(GL_TEXTURE_2D, depthTexture); 
glUniform1i(ShadowMapID, 0); 

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, texturearray);
glUniform1i(ArrayID, 1);
glUniform1i(LayerNumID1, imageIndex.x);//array index numbers
glUniform1i(LayerNumID2, imageIndex.y);
glUniform1i(LayerNumID3, imageIndex.z);

fragment shader:


uniform sampler2DShadow shadowMap; //corresponds to ShadowMapID 
uniform sampler2DArray ArraySampler; //ArrayID
uniform int layer_index1;
uniform int layer_index2;
uniform int layer_index3;
void main()
{
    vec3 MaterialDiffuseColor = texture( ArraySampler,vec3(UV, layer_index1) ).rgb;
    visibility -= 0.3*(1.0-texture( shadowMap, vec3(ShadowCoord.xy + poissonDisk[index]/700.0,  (ShadowCoord.z-bias)/ShadowCoord.w) ));

Which doesn't work. The shadow map doesn't seem to be accessible. Again, the texture array on its own is working fine, and the shadow map, when bound with individual textures, works fine as well. But, when I try to use the shadow map, while using an array for the other textures, things aren't rendering properly. To be clear though, there are no errors reported, but the shader is clearly not reading the shadowmap correctly.

If this code looks relatively fine, then my issue is likely elsewhere.

Beginner here <- please take any opinions with grain of salt

This topic is closed to new replies.

Advertisement