I have a terrain shader that needs to support texture blending for a large amount of textures, with bumpmaps.
To achieve this, I have a large amount of samplers in my fragment shader. It looks something like this:
uniform sampler2D terrainNormalsTexture; //normal texture
uniform sampler2D terrainTangentsTexture; //tangents texture, used for bump-mapping
uniform sampler2DArray terrainTextures; //each array contains 2 textures, the diffuse texture and the bumpmap texture
uniform float detailScales; //every texture can have a different UV scaling constant
uniform sampler2DArrayShadow textureShadowMap; //the array contains 5 shadowmap cascades
uniform sampler2DArray terrainAlphas; //contains 3 textures, each component (.rgba) corresponds to the blending "alpha" of the terrain textures
I’m using a bunch of sampler2DArrays in an attempt to reduce the amount of texture image units (previously these were all individual sampler2Ds, and I was going over my texture image unit target of 16).
However, I am running into an issue that sampling some of the textures will result in a complete failure of the shader. It compiles and links without error. However the terrain will not render.
For example, if I reduce the amount of terrainTextures and detailScales used (from for example 13 to 2), along with the lookups of those textures, it does work.
But increased that amount, and the terrain just completely stops rendering.
I do my texture lookups for example like this:
float alpha = texture2DArray(terrainAlphas, vec3(texCoord0,0)).r; //access the zero'th terrainAlphas, component r
Like I said this works fine up until a certain amount. For example if I would replace the last .r with .a, it could stop rendering the terrain entirely.
I know my code works in principal, since before I had bumpmaps in my code I wasn’t hitting this sort of behaviour, even with 13 terrain textures being sampled.
Could anyone give me advice on what may be causing this, and how to deal with it (or potential work-arounds)? I’m a hobbyist developer, so I may be tackling rendering using a large amount of textures the wrong way!
I can post the entire shader if requested, but it’s become a bit of a behemoth.
Much thanks in advance!
This is one of the cases where glValidateProgram can be useful. Successful linking alone does not ensure successful rendering (it doesn’t account for all other context state, which can’t be known until draw time.)
There was some particular spec language about this (emphasis added):
The LinkProgram command will attempt to determine if the active samplers in the shader(s) contained in the program object exceed the maximum allowable limits. If it determines that the count of active samplers exceeds the allowable limits, then the link fails (these limits can be different for different types of shaders)… If this cannot be determined at link time, for example if the program object only contains a vertex shader, then it will be determined at the next rendering command issued, and an INVALID OPERATION error will then be generated.
So in your example you’ve actually declared 17 samplers. At link time all of these uniforms are initialized to zero, and depending on what the rest of your shader does, not all of them are obviously active. After you’ve linked and called glUniform to point the samplers at the proper units (and possibly changed other uniform flow control in your shader), try calling glValidateProgram (just before drawing) and see if the log tells you anything.
I made a call to glValidateProgram right before rendering, and did indeed receive some interesting output:
“Validation failed! - Different sampler types for same sample texture unit in fragment shader.”
Does this mean I am somehow making a call to, for example, a sampler2D AND a sampler2DArray with the same texture unit?
Regardless, I now have a basis to work from, for which I am very thankful! I’ll continue my work on this tomorrow.
Yes, that means you have a Sampler2D and a Sampler2DArray (or Sampler2DArrayShadow) both assigned to the same unit. That’s illegal (and can’t be known at link time, because uniforms are initialized to zero.)
Double check all your glUniform calls and ensure each active sampler was assigned a unique unit (and the number of active units needs to be under the relevant limit for whichever shader stage you’re compiling, i.e. fragment shader = MAX_TEXTURE_IMAGE_UNITS.)
Got it to work
While I did have seperate texture units per sampler “reserved”, I wasn’t actually sending them all to the shader and thus, some of the “unused” texture slots for terrain painting all were using the texture index value of 0.
Just changed my code to always send in the reserved texture index, and now everything appears again.
Thanks for the assistance!