如何消除mipmapping中的纹理接缝

5

我正在使用纹理混合来实现地形片段着色器中的纹理从一种到另一种的过渡。在仅使用草纹理和混合泥土/草或雪/草纹理之间的接缝处,mipmap似乎会导致难看的接缝线(如下图所示)。禁用mipmap可以解决问题,但会使我的地形在远处非常粗糙/难看。有没有办法在不禁用mipmap的情况下消除这个接缝线呢?


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);

配置:

  • Windows 7专业版 SP1
  • Google Chrome 24.0.1312.57 m
  • NVIDIA GTX 680

普通视图 normal view

放大后的视图 zoomed in


我不知道到底是什么原因导致了这个问题。不确定它如何与mipmapping相关,因为mips本身看起来很好。只是一个随机的想法 - 你能否在main函数开始时对所有纹理进行采样,并在接下来的if语句中使用已经采样的颜色... 另外,你可能会发现一些TEXTURE_MIP_FILTER的用途。 - kerim
你能同时发布一下你的顶点着色器吗?你是否使用glGenerateMipmap生成了你的mipmaps?我还觉得奇怪的是,你在重复纹理中使用CLAMP_TO_EDGE... - Arne Bergene Fossaa
@ArneBergeneFossaa:正如您在我的纹理加载代码中所看到的,我正在使用gl.generateMipmap来生成我的mipmaps。我已经添加了地形的顶点着色器。添加/删除CLAMP_TO_EDGE似乎没有任何区别,因为我的纹理坐标从未超过1.0或低于0.0。 - Andrew Rasmussen
2个回答

8

解释原始片段着色器产生伪影的原因

GPU需要知道梯度以对纹理进行采样。你可以显式地提供梯度(textureGrad),也可以让GPU为你计算梯度。自动计算的梯度使用本地差分计算。请参见函数dFdx(http://www.opengl.org/sdk/docs/manglsl/xhtml/dFdx.xml)了解详细信息。

当函数在一个像素处评估但不在附近像素处评估时(非均匀控制流),计算导数会产生未定义的结果。

答案1中的着色器效果良好,因为纹理始终被采样,无论是否使用其结果。

更多信息请参见http://www.opengl.org/wiki/Sampler_(GLSL)#Non-uniform_flow_control


所以它们正在被采样,因为我把它们都传递给了texture2D?那么我是不是应该总是这样做,还是有时不想采样所有纹理呢?什么是梯度?我会接受你的答案,但是得到上述问题的答案会更好!谢谢! - Andrew Rasmussen
1
在这个上下文中,“梯度”(或导数)表示纹理坐标在一个像素上的变化速度。然后使用它来计算缩放因子和mipmap级别。请参阅OpenGL规范。http://www.opengl.org/registry/doc/glspec43.core.20130214.pdf第8.14.1章节。 - Maf
1
如果你真的想在非均匀控制流中采样纹理,那么就在main()函数的顶部计算梯度,并将其传递给纹理函数。vec2 dx = dFdx(texCoord); vec2 dy = dFdy(texCoord); 然后使用 textureGrad(dirt_texture, texCoord, dx, dy); 而不是 texture2D(dirt_texture, texCoord); - Maf
我在我的2D平铺引擎中花了整整30个小时寻找工件...结果发现这是微积分的错?该死!但说真的,这解释了为什么只有我的大量空白纹理出现故障。(我想) - user755921

0
据我所知,以下片段着色器基本上与我上面提供的着色器相同,但显然当传递给 a 的参数接近于 1.0 或 0.0 时,mix 函数会出现奇怪的问题。不使用 mix 函数并手动混合着色器似乎可以解决这个问题。

更新的片段着色器:

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接