0
\$\begingroup\$

I'm currently trying to map a seamless texture onto a 3d wall object but when it comes to the edges of the texture there are visible artifacts.

enter image description here

closeup:

enter image description here

Here is the shader code i use for the texture mapping:

vec4 droppedA    = calcFloating(v_model_normal, v_model_position);
vec4 albedo_drop = calcDropped(droppedA, u_AlbedoMapRotation, u_AlbedoMapScale.xy);

float level = 0.0;
float size = 2048.0;
float threshold = 1.0 / size;

if(albedo_drop.x < threshold || albedo_drop.x > 1.0 - threshold || albedo_drop.y < threshold || albedo_drop.y > 1.0 - threshold) {
    level = -8.0;
}

vec4 albedoA     = texture2D(u_AlbedoMap, albedo_drop.xy, level);

Now unfortunately i did not write the code for the methods calcFloating and calcDropped (i know, shame on my head) but i know it's a general algorithm to calculate the texture coordinates for any given 3d object. I can provide the code for these but for the sake of simplicity (the code is quite long) i will leave it out for now. I assume the problem lies somewhere else nonetheless, because for non repeated textures it works perfectly fine.

So going on with the calculated coordinates. At first, I thought the problem is because of the calculated mip maps and that for some reason (maybe floating point imprecisions) the texture lookup choses the wrong mip map texture level. The if statement basically changes the bias for mip map level only for the edges. (size = 2048.0 because the textures size is 2048*2048 pixel) I tried different biases for the edges (always with the same zoom and camera viewing direction/position of course) but none of them worked (there were just slight changes in the look of the artifact).

Another important note to mention! I still believe it has something to do with the mip maps because when i dont't use mip maps and set the MIN and MAG filter to GL_Linear or GL_Nearest, then the texture repeat is perfectly seamless. (which is another reason for why i don't think that the problem lies within the calcFloating or calcDropped function)

Some facts about my texture parameter setup:

GL_TEXTURE_MIN_FILTER : GL_LINEAR_MIPMAP_LINEAR

GL_TEXTURE_MAG_FILTER : GL_LINEAR

GL_WRAP_S : GL_REPEAT

GL_WRAP_T : GL_REPEAT

Texture size : 2048 * 2048 pixel

By the way, I am using threejs which uses a WebGL 1 context by default.

I am thankful for any help

=========================================================

EDIT: Here's the full code on how i calculate my UV's (Without my own mipmap level computation, which was just for debugging purposes)

vec4 calcFloating(vec3 viewDir, vec3 position2)
{
  vec4 dropped  = vec4(0.0,0.0,1.0,1.0);
  vec3 viewDir2 = abs(viewDir);
  if (viewDir2.z > viewDir2.y && viewDir2.z > viewDir2.x) {
    dropped.xy  = position2.yx;
    if (viewDir.z > 0.0) {
      dropped.y = -dropped.y;
      dropped.w = -dropped.w;
    }
    else
    {
      dropped.w = -dropped.w;
    }
  } else if (viewDir2.y > viewDir2.z && viewDir2.y > viewDir2.x) {
    dropped.xy  = position2.zx;
    if (viewDir.y < 0.0) {
      dropped.y = -dropped.y;
      dropped.w = -dropped.w;
    }else
    {
      dropped.w = -dropped.w;
    }
  } else if (viewDir2.x > viewDir2.z && viewDir2.x > viewDir2.y) {
    dropped.xy  = position2.zy;
    if (viewDir.x > 0.0) {
      dropped.y = -dropped.y;
      dropped.w = -dropped.w;
    }else
    {
      dropped.w = -dropped.w;
    }
  }
  return dropped;
}

vec4 mapToRepeatUV(vec4 dropped)
{
  dropped.x = dropped.x - floor(dropped.x);
  dropped.y = dropped.y - floor(dropped.y);
  return dropped;
}

vec4 roatateUV(vec4 dropped, float a_deg)
{
  float a_pi = a_deg / 180.0 * PI;
  dropped.xy    = vec2(dropped.x * sin(a_pi) - dropped.y * cos(a_pi), dropped.x * cos(a_pi) + dropped.y * sin(a_pi));
  return dropped;
}

vec4 calcDropped(vec4 dropped, float a_deg, vec2 scale)
{
  vec4 ret = roatateUV(dropped, a_deg);
  return mapToRepeatUV(ret / vec4(scale.x,scale.y,1.0,1.0));
}

vec4 droppedA    = calcFloating(v_model_normal, v_model_position);
vec4 albedo_drop = calcDropped(droppedA, u_AlbedoMapRotation, u_AlbedoMapScale.xy);
vec4 albedoA     = texture2D(u_AlbedoMap, albedo_drop.xy);

Notes on the arguments handed into calcFloating and calcDropped:

u_AlbedoMapRotation: texture rotation uniform -> is set 0.0, texture is not rotated

u_AlbedoMapScale: texture scaling uniform -> x and y is set to 1000.0 (wall is 4000.0mm (4m) wide) resulting in 4 repeated textures in width and height (see first image)

v_model_normal: varying for the vertex normal

v_model_position: varying for vertex position

\$\endgroup\$
  • \$\begingroup\$ Hi, Any reason why you do the mipmap level computation by hand rather than let OpenGL do it? If so, why set the level to -8 on the texture borders? \$\endgroup\$ – gan_ Feb 22 at 10:53
  • \$\begingroup\$ Did you call glGenerateMipmaps? \$\endgroup\$ – Bálint Feb 22 at 10:58
  • \$\begingroup\$ @gan_ I do the mip map level computation by hand because i think/thought that OpenGL did it wrong for the edges (due to imprecisions maybe?). The -8.0 is just the max mip level and is just a value i tried out. I tried different values between 0.0 and -8.0 at that line \$\endgroup\$ – Tom B. Feb 22 at 11:04
  • \$\begingroup\$ @Bálint Yes, glGenerateMipmaps is called internally by threejs when you set the min filter to some mipmap value (e.g. LinearMipMapLinear). The Mipmaps are also working fine, just not on the edges of the texture \$\endgroup\$ – Tom B. Feb 22 at 11:07
  • 1
    \$\begingroup\$ @Tom B. This happens because your UVs are not continuous. OpenGL should never fail to compute the adequate mipmap level for continuous UVs. Can you show how you compute your UVs? \$\endgroup\$ – gan_ Feb 22 at 11:10

Your Answer

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

Browse other questions tagged or ask your own question.