title: GLSL: Choosing which texture to use in the fragment shader by indexing into a uniform array of structs based on the world-position of the fragment
body: <p>I am working on a C++ program which displays a terrain mesh using GLSL shaders. I want it to be able to use different materials based on the elevation.  </p>
  
  <p>I am trying to accomplish this by having an uniform array of materials in the fragment shader and then using the y coordinate of the world-space position of the current fragment to determine which material from the array to use.</p>
  
  <p>Here are the relevant parts of my fragment shader:</p>
  
  <pre><code>#version 430
  
  struct Material
  {
      vec3 ambient;
      vec3 diffuse;
      vec3 specular;
      int shininess;
      sampler2D diffuseTex;
      bool hasDiffuseTex;
      float maxY; //the upper bound of this material's layer in relation to the height of the mesh (in the range 0-1)
  };
  
  in vec2 TexCoords;
  in vec3 WorldPos;
  
  const int MAX_MATERIALS = 14;
  
  uniform Material materials[MAX_MATERIALS];
  uniform int materialCount; //the actual number of materials in the array
  
  uniform float minY; //the minimum world-space y-coordinate in the mesh
  uniform float maxY; //the maximum world-space y-coordinate in the mesh
  
  out vec4 fragColor;
  
  void main()
  {
  
      //calculate the y-position of this fragment in relation to the height of the mesh (in the range 0-1)
      float y = (WorldPos.y - minY) / (maxY - minY);
  
      //calculate the index into the materials array 
      int index = 0;
      for (int i = 0; i &lt; materialCount; ++i)
      {
          index += int(y &gt; materials[i].maxY);
      }
  
      //calculate the ambient color
      vec3 ambient = ...
  
      //calculate the diffuse color
      vec3 diffuse = ...
      //sample from the texture
      vec3 texColor = vec3(texture(materials[index].diffuseTex, TexCoords.xy));
      //only multiply diffuse color with texture color if the material has a texture
      diffuse += int(materials[index].hasDiffuseTex) * ((texColor * diffuse) - diffuse);
  
      //calculate the specular color
      vec3 specular = ...
  
      fragColor = vec4(ambient + diffuse + specular, 1.0f);
  }
  </code></pre>
  
  <p>It works fine if textures are not used: </p>
  
  <p><img src="https://i.stack.imgur.com/5UmIA.png" alt="Mesh without texture"></p>
  
  <p>But if one of the materials has a texture associated with it, it shows some black artifacts near the borders of the material layer which has the texture:</p>
  
  <p><img src="https://i.stack.imgur.com/4ujPp.png" alt="Mesh with texture"></p>
  
  <p>When I add this line after the diffuse calculation part:</p>
  
  <pre><code>if (index == 0 &amp;&amp; int(materials[index].hasDiffuseTex) == 1 &amp;&amp; texColor == vec3(0, 0, 0)) diffuse = vec3(1, 0, 0);
  </code></pre>
  
  <p>it draws the artifacts in red:</p>
  
  <p><img src="https://i.stack.imgur.com/cRjFi.png" alt="Mesh with texture 2"></p>
  
  <p>which tells me that the index is correct (0) but nothing is sampled from the texture. </p>
  
  <p>Furthermore if I hardcode the index into the shader like this:</p>
  
  <pre><code>vec3 texColor = vec3(texture(materials[0].diffuseTex, TexCoords.xy));
  </code></pre>
  
  <p>it renders correctly. So I am guessing it has something to do with the indexing but the index appears to be correct and the texture is there so why doesn't it sample color? </p>
  
  <p>I have also found out that if I switch the order of the materials and move their borders around in the GUI of my program in a certain fashion it starts to render correctly from that point on which I don't understand at all. I first suspected that this might be due to me sending wrong values of uniforms to the shaders initially and then somehow it gets the correct ones after I make the changes in the GUI but then I have tested all the uniform values I am sending to the shader from the C++ side and they all appear to be correct from the start and I don't see any other possible problem which might cause this from the C++ side. So I am now thinking the problem is probably in the shader.</p>