How to eliminate texture seams from mipmapping
Asked Answered
V

2

5

I'm using texture blending in my terrain's fragment shader to blend from one texture to the next. Right at the seam between using only my grass texture and blending between dirt/grass or snow/grass textures, the mipmaps seem to cause an ugly seam (see photo below). Disabling mipmapping fixes the problem but makes my terrain very grainy/ugly at a distance. Is there a way to eliminate this seam without disabling mipmapping?


terrain-vs.glsl:

precision mediump float;

attribute vec3 Position;
attribute vec2 TextureCoord;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

varying vec2 texCoord;
varying float y;

void main(void) {
  gl_Position = uPMatrix * uMVMatrix * vec4(Position, 1.0);
  texCoord = TextureCoord;
  y = Position.y;
}

terrain-fs.glsl:

precision mediump float;

uniform sampler2D dirt_texture;
uniform sampler2D grass_texture;
uniform sampler2D snow_texture;

varying vec2 texCoord;
varying float y;

void main(void) {
  if (y < -5.0) {
    gl_FragColor = texture2D(dirt_texture, texCoord);
  } else if (y < 0.0) {
    gl_FragColor = mix(
      texture2D(dirt_texture, texCoord),
      texture2D(grass_texture, texCoord),
      (y + 5.0) / 5.0
    );
  } else if (y < 3.0) {
    gl_FragColor = texture2D(grass_texture, texCoord);
  } else if (y < 5.0) {
    gl_FragColor = mix(
      texture2D(grass_texture, texCoord),
      texture2D(snow_texture, texCoord),
      (y - 3.0) / 2.0
    );
  } else {
    gl_FragColor = texture2D(snow_texture, texCoord);
  }
}

TextureManager::initialize

gl.bindTexture(gl.TEXTURE_2D, texture.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null);

Configuration:

  • Windows 7 Pro SP1
  • Google Chrome 24.0.1312.57 m
  • NVIDIA GTX 680

normal view normal view

zoomed in zoomed in

Venose answered 8/2, 2013 at 4:8 Comment(3)
I have no idea what actually could cause this. Not sure how it can relate to mipmapping as mips themselves look good. Just a random thought - could you try sampling all your textures at the beginning of main and use already sampled colors in the following if... Also you could probably find some use of TEXTURE_MIP_FILTERPaddie
Could you post your vertex svarer as well? Have you generated your mipmaps with glGenerateMipmap? I also find it weird that you are using CLAMP_TO_EDGE for a repeating texture...Superman
@ArneBergeneFossaa: As you can see in my texture loading code, I am using gl.generateMipmap to generate my mipmaps. I've added my vertex shader for the terrain. Adding/removing CLAMP_TO_EDGE doesn't seem to make any difference since my texture coords never go above 1.0 or below 0.0.Venose
M
8

Explanation why the original fragment shader produces artifacts

GPU needs to know gradients to sample a texture. You either supply the gradients explicitly (textureGrad) or you let GPU to compute the gradients for you. The automatically computed gradient are computed using local differencing. See function dFdx (http://www.opengl.org/sdk/docs/manglsl/xhtml/dFdx.xml) for details.

Computing derivatives produces undefined result when the function evaluates for one pixel but not for nearby pixels (non uniform control flow).

The shader in Answer 1 works well because the textures are always sampled, no matter if their result is used or not.

More info is here http://www.opengl.org/wiki/Sampler_(GLSL)#Non-uniform_flow_control

Moina answered 17/2, 2013 at 17:25 Comment(4)
So they are being sampled because I'm passing them all in to texture2D? Should I always do this then, or is there ever a time where I wouldn't want to sample all of my textures? What exactly is a gradient? I'll accept your answer, but it would be great to get answers to the above questions! Thank you!Venose
Gradient (or derivation) in this context indicates how fast the texture coordinate changes over one pixel. This is then used to compute the scaling factor and mipmap level. See OpenGL spec. opengl.org/registry/doc/glspec43.core.20130214.pdf chapter 8.14.1Moina
If you really want to sample texture inside non uniform control flow, then compute the gradient at the top of the main() and pass it to texture functions. vec2 dx = dFdx(texCoord); vec2 dy = dFdy(texCoord); and then use textureGrad(dirt_texture, texCoord, dx, dy); instead of texture2D(dirt_texture, texCoord);Moina
I've spent literally 30 hours hunting down artifacts in my 2D tiling engine... it turns out that it's calculus' fault? Drat! Seriously though, this explains why only my textures with tons of whitespace were glitching out. (I think)Reward
V
0

To the best of my knowledge, the following fragment shader is basically identical to the one I supplied above, but apparently mix does funky things when the parameter passed in for a is near 1.0 or 0.0. Not using mix and manually mixing the shaders seemed to fix this.


Updated fragment shader:

precision mediump float;

uniform sampler2D dirt_texture;
uniform sampler2D grass_texture;
uniform sampler2D snow_texture;

varying vec2 texCoord;
varying float y;
void main(void) {
  vec4 dirt = texture2D(dirt_texture, texCoord);
  vec4 grass = texture2D(grass_texture, texCoord);
  vec4 snow = texture2D(snow_texture, texCoord);
  float dirt_weight = 0.0;
  float grass_weight = 0.0;
  float snow_weight = 0.0;
  if (y < -5.0) {
    dirt_weight = 1.0;
  } else if (y < 0.0) {
    grass_weight = (y + 5.0) / 5.0;
    dirt_weight = 1.0 - grass_weight;
  } else if (y < 3.0) {
    grass_weight = 1.0;
  } else if (y < 5.0) {
    snow_weight = (y - 3.0) / 2.0;
    grass_weight = 1.0 - snow_weight;
  } else {
    snow_weight = 1.0;
  }
  gl_FragColor = dirt * dirt_weight + grass * grass_weight + snow * snow_weight;
}
Venose answered 16/2, 2013 at 21:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.