1
\$\begingroup\$

I'm trying to do basic multitexturing of terrain in OpenGL.

Im building the terrain with an image representing different elements (beach, water, jungle...) :

Terrain

Im trying to map each color of this image with a texture in my game: green color means grassTexture (id 0), blue color means waterTexture (id 1), etc...

My fragment shader contains an uniform array containing all textures that I use (grass, water, ...) :

// Terrain.frag uniform sampler2D terrainTextures[50];

For each vertex of the terrain, I pass a float in_TextureId corresponding to a texture id :

// Terrain.vert
layout (location = 0) in vec3 in_Vertex;
layout (location = 1) in vec4 in_Color;
layout (location = 2) in vec3 in_Normal;
layout (location = 3) in vec2 in_TexCoord;
layout (location = 4) in float in_TextureId;

Then im trying to display the correct texture for a vertex with this textureId in the fragment shader :

outputColor = texture(terrainTextures[int(textureId)], texCoord);

With this line, I have this result :

Weird result

If I modify the code of the fragment shader with the following code :

highp int tid = int(textureId);
if(tid == 2)
{
   outputColor = texture(terrainTextures[int(textureId)], texCoord);        
}
else if(tid == 1)
{
   outputColor = texture(terrainTextures[int(textureId)], texCoord);
}
else outputColor = texture(terrainTextures[int(textureId)], texCoord);`

It works better :

Good result

I can't understand why it's working with this code, i'm doing exactly the same thing in the first example.

Maybe it's not the good way to access an uniform array ?

\$\endgroup\$
  • 1
    \$\begingroup\$ Looks like a bug in the compiler to me. I've seen it happen from time to time. \$\endgroup\$ – user1118321 Aug 1 '18 at 4:26
  • 1
    \$\begingroup\$ Have you tried floor(textureId) instead of int(textureId)? \$\endgroup\$ – cozmic Aug 1 '18 at 5:41
  • 1
    \$\begingroup\$ Have you tried using the "flat" qualifier for your texture id? why not use 2d texture arrays? \$\endgroup\$ – Kyy13 Aug 1 '18 at 14:14
  • \$\begingroup\$ Yes I tried flat qualifier and floor(textureId), still the same :(. I will try texture arrays (and will come back to validate the answer), it's the first time I do multitexturing. Thanks. \$\endgroup\$ – Robert Aug 1 '18 at 21:31
3
\$\begingroup\$

I'm not 100% sure exactly what the cause of the problem is, but I know the "right" way to do what you're trying to achieve. But I think the problem is that an array of samplers is only supposed to be indexed with a compile time constant. In your if-else version, the compiler is probably able to figure out that there's only one possible answer in each case and replaces it with a constant.

As for what to do instead:

The best way to choose between multiple 2D textures of the same size in a shader is using a texture array. They are loaded like 3D textures, and let you extremely efficiently choose between textures in the shader.

You'll use a sampler2DArray instead of a sampler2D, and you'll use a 3D texture co-ordinate instead of 2D -- the third dimension is the index of the texture you want to use.

In this talk Approaching Zero Driver Overhead, representatives from the major GPU vendors come together to talk about the fastest way to do things. Texture Arrays (or Array Textures) are just about the easiest thing on the list, and are super fast.

\$\endgroup\$
  • 1
    \$\begingroup\$ "only supposed to be indexed with a compile time constant" OpenGL 4.x allows a dynamically uniform expression. Which what he is trying to do is not. \$\endgroup\$ – Nicol Bolas Aug 3 '18 at 13:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.