Trilinear filtering on texture array

Hi !

I have some weird results with trilinear filter (LINEAR_MIPMAP_LINEAR) using texture array.

Example : Texture pattern is 2x2 circle which is tiled on a quad. You can see some artefacts on tiled edge.

With bilinear (LINEAR) filter :

[img=http://img218.imageshack.us/img218/3586/bilinear.th.png]

With trilinear (LINEAR_MIPMAP_LINEAR) filter :

[img=http://img55.imageshack.us/img55/6450/trilinear.th.png]

Mipmap are generated with glComputeMipmap(). Mipmap value are right (I have checked their values with texelFetch).

WRAP_S and WRAP_T are set with CLAMP with “fract(texCoord)” in shader. I have done some test with CLAMP_TO_EDGE and CLAMP_TO_BORDER but results are the same.

I have no idea where I’ve done a mistake. Any ideas ?

Thanks in advance.

(Sorry for my bad english)

And if change only the texture array to a single classic texture ?

It is the same. Thus problem doesn’t come from texture array.

I think problem is :

vec2 realCoord = fract(LocalTexCoord);

Because when I set wrap_s and wrap_t with GL_REPEAT and I replace line above with :

vec2 realCoord = LocalTexCoord;

I get the correct result. But I need to clamp texture because I store some coefficients in texture and they are not interpolable on border.

Any idea ?

Thanks.

[0,1[ (fract) interval does cross the border.
you will have to step back half a texel, to stay within this interval :
[texelsize/2,1-texelsize/2]

You have to manually emulate GL_CLAMP_TO_EDGE behaviour to completely avoid border interpolation.

possible way, for a square texture :


vec2 realCoord = fract(LocalTexCoord);
realCoord.x = clamp(realCoord.x,half_texelsize,1.0-half_texelsize);
realCoord.y = clamp(realCoord.y,half_texelsize,1.0-half_texelsize);

of course texelsize depends on the texture size:
texelsize = 1.0/texturesize

Thanks for your reply. But I’m not sure to understand.

For example : To render a texture on a quad you set bascilly vertices with texture coodinate like :


vertex0 : 0.0,0.0
vertex1 : 1.0,0.0
vertex2 : 1.0,1.0
vertex3 : 0.0,1.0

These coordinate are interpolated just before fragment shader. So when you are in fragment shader, your texture coordinate are in interval [0.0,1.0]. Aren’t they?

That’s why I don’t understand why you say texture coordinates processed with fract (transformed in [0.0,1.0[) cross the border.

What is this interval : [texelsize/2,1-texelsize/2] ?

Thanks.

Inclusive or exclusive does not mean a lot with floats.
It is just that fract() returns only the fractional part, 0.0 inclusive, 1.0 exclusive. Whatever.

Read a bit about the different clamp modes : texcoord 0.0 is in fact between the edge texel E (the first one you define) and the border texel B (either defined explicitely with border=1 or indirectly with border color). By default border color is black. So for a pure white texture, naive GL_CLAMP with let some 50% grey at texcoord 0.0 and 1.0. GL_CLAMP_TO_EDGE clamps texcoord earlier (half a texel) to prevent this color bleeding. GL_REPEAT does not have this problem, as the B texel is in fact the E texel from the opposite side of the texture.
You need exactly a combination of GL_REPEAT with GL_CLAMP_TO_EDGE.

Also described is a very recent post here :
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=259108#Post259108

Is that clearer now ?

Using fract leads to the artefacts you see because it messes up the texcoord derivatives. The texture unit uses these to determine the texture level of detail that should be used.

When you use fract, the relatively small step between e.g. 0.99 and 1.01 (+0.02) becomes a step from 0.99 to 0.01, i.e. -0.98. If this happens between two neighbouring fragments, the GL implementation will believe that you want to map almost the entire texture in between those two fragments. In order to avoid aliasing it will then select the lowest resolution mipmap level. That’s what causes those grey pixels.

There are a number of things you could do, however before I suggest one it would be good if you could explain what the coefficients represent, why you can’t interpolate them across texture edges, and how you intend to deal with the discontinuity edge that will inevitably arise from repeating the texture without filtering across edges.

Xmas is right, I already had the same problem.

If you absolutely need to use fract(), then what you’ll have to do is to compute the mipmap level yourself, then sample the texture manually for that level. Here’s an example that should set you on the right track:


/// This function evaluates the mipmap LOD level for a 2D texture using the given texture coordinates
/// and texture size (in pixels)
float mipmapLevel(vec2 uv, vec2 textureSize)
{
    vec2 dx = dFdx(uv * textureSize.x);
    vec2 dy = dFdy(uv * textureSize.y);
    float d = max(dot(dx, dx), dot(dy, dy));
    return 0.5 * log2(d);
}

...
float lod = mipmapLevel(uv, 1024.0);
vec4 col = texture2DLod(tex, fract(uv), lod);


Y.

Is that clearer now ?

I think it is. Thanks for your time !

There are a number of things you could do, however before I suggest one it would be good if you could explain what the coefficients represent, why you can’t interpolate them across texture edges, and how you intend to deal with the discontinuity edge that will inevitably arise from repeating the texture without filtering across edges.

I store some spherical harmonics (SH) in texture. I have two kinds of texture :

  • [li]Spatial SH textures : Store SH coefficients per position (actually texel position : s,t) on quad. []Angular SH textures : Store SH coefficients per spherical coordinates (theta[0,PI/2],phi[0,2PI]) (for half vector).

For these textures, I would like trilinear interpolation inside textures but not on border.

Pictures above show artifact on spatial textures. I haven’t mentionned it before, but I have same artifacts with angular textures when phi is around 2*PI.

Thus, what do you suggest?

Thanks.