Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I just started learning openGL and I am getting this artifact when texturing a sphere with mipmaps. Basically when the fragment samples the edge of my texture, it detects the discontinuity (say from 1 to 0) and picks the smallest mipmap, which creates this ugly seam:

ugly seam

So I tried to manually override the gradients using textureGrad:

//fragVert is the original vertex from the vertex shader
vec2 ll = vec2((atan(fragVert.y, fragVert.x) / 3.1415926 + 1.0) * 0.5, (asin(fragVert.z) / 3.1415926 + 0.5));
vec2 ll2 = ll;
if (ll.x < 0.01 || ll.x > 0.99)
    ll2.x = .5;
vec4 surfaceColor = textureGrad(material.tex, ll, dFdx(ll2), dFdy(ll2));

Now I get two seams instead of one. How can I get rid of them? And why does the code above generate 2 seams?

2 eams

You can't tell from the two images but the 2 seams are on either side of the original seam.

share|improve this question
2  
Is your sphere a mesh, or are you raytracing the sphere analytically in the shader or suchlike? If it's a mesh, people usually solve this by duplicating the vertices along the seam so that the verts on one side can have u = 0 and the other side have u = 1. –  Nathan Reed May 31 '13 at 23:30
    
As for why your code generates two seams, I expect it's because it's creating two discontinuities - one where u jumps from 0.01 to 0.5, and one where u jumps from 0.99 to 0.5. It's the same problem as when u jumps from 0 to 1 - not as large of a jump but still a big jump. –  Nathan Reed May 31 '13 at 23:32
    
I have a mesh for the sphere. It's a decahedron so the vertices don't line up to the seam. The funny thing about the two seams is the original seam is gone. Also, I get the same 2 seams even if I clamp it to .01 or .99. –  omikun Jun 1 '13 at 0:26
    
Can you post a screenshot of the two-seams case? And, how are you generating the UVs (can you post the relevant part of your code)? I presume you are generating them procedurally in the shader, not just interpolating them from the vertices, else your dodecahedron mesh wouldn't work at all. –  Nathan Reed Jun 1 '13 at 0:31
    
I updated the question with a picture of the 2 seams, they're either side of the original seam, but the original seam isn't there . I think the the UV are interpolated from the vertices, not sure. The code is in the question now. –  omikun Jun 1 '13 at 2:57
add comment

1 Answer

up vote 3 down vote accepted

Based on the shader code you posted, you're not interpolating the UVs from the vertices - rather it appears you're interpolating the 3D position (fragVert), then calculating the UVs by transforming to spherical coordinates.

Your analysis is correct in that the smallest mipmap is chosen when there is a discontinuity, since mipmap selection is based on derivatives that are estimated numerically from the UVs used in neighboring pixels. When one pixel has u = 0 and another has u = 1 you get a very large derivative. Your attempted fix has the same issue in that large derivatives occur around u = 0.01 and u = 0.99, which is why two seams appear on either side of where the original seam was.

A relatively simple approach to fix the problem would be to decide which mip level to use yourself and call textureLod to sample it directly. If the planet is always going to be fairly close to the camera, you could just hard-code the mip level to 0 (or, for that matter, just not include mip levels in the texture at all). Otherwise, it could be based on the log2 of the point's distance from the camera, scaled by some suitable factors. Note that this will effectively disable anisotropic filtering.

A more "correct" approach would be to calculate some higher-quality derivatives. Instead of using dFdx and dFdy on the UVs, which have discontinuities due to the atan2, you could apply dFdx and dFdy to fragVert (which will be continuous all the way around the sphere), then use some calculus (chain rule) to find the formula to get the UV derivatives from the position derivatives. This will be more complicated and slower, but has the advantage that anisotropic filtering should work.

Finally, since you're new to OpenGL I'll just note that while calculating UVs from spherical coordinates is a perfectly valid way to texture a sphere, it's not the "usual" way that most people pick. It's more common to build a sphere mesh that has UVs specified per vertex, and simply passed from the vertex shader to the pixel shader (linearly interpolated across each triangle). Vertices are placed along the seam, like this, such that there are two copies of each vertex, at exactly the same positions, but half with u = 0 connected to the triangles on one side, and the other half with u = 1 connected to the triangles on the other side. This eliminates any visible seam and doesn't require any tricks in the pixel shader.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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