|
View previous topic :: View next topic
|
| Author |
Message |
skynet
Joined: 01 Sep 2008 Posts: 20
|
| |
Posted: Tue Dec 08, 2009 6:05 pm Post subject: Anisotropic filtering |
 |
|
Hi!
I'm trying to figure out how to do anisotropic filtering with virtual texturing. I have a "virtual tile overlapping border" of 4 texels in each direction. That should be enough to do 8x Anisotropic filtering.
I'm just uncertain, how
By looking through the specs of EXT_texture_filter_anisotopic, I see, there are two phases for an anisotropic texture lookup
1. Select a proper mipmap level
2. Do up to max_anisotropy 'taps' along te longest axis of the pixel footprint in the texture
1) has to be done at the pagetable texture. As this texture needs to be sampled with NEAREST filtering, I have to emulate the mipmap selection myself and use texture2DLod() to access it with GLSL.
The EXT_t_f_a specs seem to select a mipmap level that spaces the taps around 1 texel apart.
2) has to be done in the pagecache texture (where the physical pages reside). I'm hoping I just can get into it with th physical coordinates; enabling 8x anisotropy for this texture should trick the hardware into doing up to 8 taps for me. I'll probably have to use texture2DGrad() here, in order to circumvent problems, when two adjacent pixels refer to different physical pages. But I'm unsure _what_ gradient I have to use here (at which scaling factor!) in order to get proper anisotropic sampling.
Did anyone of you did this before? Are there even more pitfalls to care about? |
|
| Back to top |
|
 |
skynet
Joined: 01 Sep 2008 Posts: 20
|
| |
Posted: Tue Dec 08, 2009 7:26 pm Post subject: |
 |
|
I did some more tweaking and now it seems to work. The whole anisotropic texture access looks now this way:
| Code: |
uniform float u_max_anisotropy = 8.0;
uniform sampler2D t_pagetable;
uniform sampler2D t_pagecache;
//calculate the base mipmap level needed for the current anisotropy (according to GL specs)
vec2 ddx=dFdx(gl_TexCoord[0].xy);
vec2 ddy=dFdy(gl_TexCoord[0].xy);
float Px = length(ddx);
float Py = length(ddy);
float Pmax = max(Px, Py);
float Pmin = min(Px, Py);
float N = min(ceil(Pmax/Pmin), u_max_anisotropy);
float lambda = log2(Pmax / N); // this is probably some negative value, because scaling Pmax by texture size is missing
// lambda will by biased by log2(pagesize)+log2(texturesize) through glTexParameteri
vec3 vt_to_cache = textureLod(t_pagetable, gl_TexCoord[0].xy, lambda).xyz;
// translate virtual coordinates into physical coordinates, fract will make the whole VT wrap around and repeat properly
vec2 phys_tc = fract(gl_TexCoord[0].xy) * vt_to_cache.x + vt_to_cache.yz;
// prevent artifacts when neighbouring pixels access different physical pages by using the gradients of the VT-coordinates, scaling is needed because anisotropic filtering is using the length of ddx/ddy to place the taps
vec4 texcolor = textureGrad(t_pagecache, phys_tc, ddx*vt_to_cache.x, ddy*vt_to_cache.x);
|
|
|
| Back to top |
|
 |
sean
Joined: 01 Feb 2005 Posts: 1392 Location: Kirkland WA
|
| |
Posted: Wed Dec 09, 2009 12:50 am Post subject: |
 |
|
So, I assume that's actually exactly the same scaling you would use if you were trying to get bilinear filtering but using textureGrad(), right?
At least, as far as I can see, it shouldn't be any different in theory. |
|
| Back to top |
|
 |
skynet
Joined: 01 Sep 2008 Posts: 20
|
| |
Posted: Wed Dec 09, 2009 10:32 am Post subject: |
 |
|
Yes, the idea behind "ddx*vt_to_cache.x, ddy*vt_to_cache.x" is to stretch the pixel footprint from pagetable-coords into physical coords, so the spacing of the taps in the page cache texture is correct.
textureGrad() should not be needed for pure bilinear filtering, at all. In the case of bilinear filtering, the page cache texture has just 1 level. In this case, the texture minification filter for the page cache is set to GL_LINEAR, which completely ignores the gradients.
This is as far as I can see enough to get simple bilinear filtering:
| Code: |
uniform sampler2D t_pagetable;
uniform sampler2D t_pagecache;
//t_pagetable has a LOD bias of log2(pagesize)
vec3 vt_to_cache = texture2D(t_pagetable, gl_TexCoord[0].xy).xyz;
// translate virtual coordinates into physical coordinates
vec2 phys_tc = fract(gl_TexCoord[0].xy) * vt_to_cache.x + vt_to_cache.yz;
// access physical page
vec4 texcolor = texture2D(t_pagecache, phys_tc);
|
|
|
| Back to top |
|
 |
julian
Joined: 17 Oct 2008 Posts: 38
|
| |
Posted: Wed Dec 09, 2009 3:49 pm Post subject: |
 |
|
thanks, i was experimenting with anisotropy as well and i had some issues.
reading your posts my issue seems to be that i completely ignored issue 1. emulating mip-map selection.
anyway since anisotropy modifies the required mip-map levels, i guess one has to modify the pre-pass too, so the right tiles are fetched?
i've changed over my pre-pass from "calculate mip and coordinates myself" to "determine mip coordinates by sampling from a texture" because the manual calculation never really matches what OpenGL does. (even after fixing the version in SVT, which seemed to be buggy - anyone else had this issue?) i guess this is method is no longer possible since i can't enable anisotropy for this texture... |
|
| Back to top |
|
 |
skynet
Joined: 01 Sep 2008 Posts: 20
|
| |
Posted: Wed Dec 09, 2009 4:49 pm Post subject: |
 |
|
| Quote: | | anyway since anisotropy modifies the required mip-map levels, i guess one has to modify the pre-pass too, so the right tiles are fetched? |
This is something I still have to do. I did kind of a reverse approach to the whole VT stuff... I started with an out-of-core memory managment for the tiles, then built tools to create the textures on disk. I have now implemented PageTable/PageCache, but the real fun (generating page requests for the current view) has still to be done
| Quote: | | i guess this is method is no longer possible since i can't enable anisotropy for this texture... |
If this is a texture which encodes the mipmap level as color (0,1,2,3,4,5 etc), you should be able to just enable anisotropic filtering on it. Averaged multiple taps within one level are no problem ((2+2+2+2)/4 is still 2). Trilinear filtering (where each tap can again be an average of sampling at two levels) might get crazy, though... |
|
| Back to top |
|
 |
julian
Joined: 17 Oct 2008 Posts: 38
|
| |
Posted: Wed Dec 09, 2009 5:11 pm Post subject: |
 |
|
| skynet wrote: |
If this is a texture which encodes the mipmap level as color (0,1,2,3,4,5 etc), you should be able to just enable anisotropic filtering on it. Averaged multiple taps within one level are no problem ((2+2+2+2)/4 is still 2). Trilinear filtering (where each tap can again be an average of sampling at two levels) might get crazy, though... |
yeah i thought so much. although i also encode the tile coordinates in the texture, which might not work so fine with the averaging.
i'll do some tests now to see how the anisotropy changes the mip selection... |
|
| Back to top |
|
 |
sean
Joined: 01 Feb 2005 Posts: 1392 Location: Kirkland WA
|
| |
Posted: Wed Dec 09, 2009 6:56 pm Post subject: |
 |
|
| skynet wrote: | | textureGrad() should not be needed for pure bilinear filtering, at all. In the case of bilinear filtering, the page cache texture has just 1 level. In this case, the texture minification filter for the page cache is set to GL_LINEAR, which completely ignores the gradients. |
Yeah, I wasn't saying it was necessary, just that it's the same formula you would use -- there's nothing particularly surprising about the necessary gradients. (And they do become necessary to use for trilinear, though.) |
|
| Back to top |
|
 |
julian
Joined: 17 Oct 2008 Posts: 38
|
| |
Posted: Wed Dec 09, 2009 7:05 pm Post subject: |
 |
|
| it seems anisotropy changes the mip selection for GL_*_MIPMAP_LINEAR, but not for GL_*_MIPMAP_NEAREST |
|
| Back to top |
|
 |
julian
Joined: 17 Oct 2008 Posts: 38
|
| |
Posted: Tue Mar 02, 2010 12:22 am Post subject: |
 |
|
| skynet wrote: | I did some more tweaking and now it seems to work. The whole anisotropic texture access looks now this way:
|
hm i extracted your LoD selection in a function:
| Code: |
float mipmapLevel_anis(vec2 uv, vec2 textureSize)
{
vec2 ddx = dFdx(uv);
vec2 ddy = dFdy(uv);
float Px = length(ddx);
float Py = length(ddy);
float Pmax = max(Px, Py);
float Pmin = min(Px, Py);
float N = min(ceil(Pmax/Pmin), 8.0);
float lambda = log2(Pmax / N); // this is probably some negative value, because scaling Pmax by texture size is missing
// lambda will by biased by log2(pagesize)+log2(texturesize) through glTexParameteri
return lambda + mip_calculation_bias;
}
|
but it is not returning results anything like actual mip selection from a texture, with or without anisotropy, regardless of bias. did you test this somehow? btw what should texturesize be?
i have some more success when i change Px, Py to:
float Px = sqrt(ddx.x * ddx.x + ddy.x * ddy.x);
float Py = sqrt(ddx.y * ddx.y + ddy.y * ddy.y);
but still the results are only similar to actual mipselection at the screen borders.
btw all other mipmapLevel calculation functions i've seen multiply the UV coordinates by (virtual)texturesize before passing to dFdx/y... |
|
| Back to top |
|
 |
sean
Joined: 01 Feb 2005 Posts: 1392 Location: Kirkland WA
|
| |
Posted: Tue Mar 02, 2010 4:43 am Post subject: |
 |
|
| Quote: | | it is not returning results anything like actual mip selection from a texture, with or without anisotropy, regardless of bias. |
Here's the mip-level function in mine (which doesn't do anisotropic):
| Code: | float tex_mip_level(vec2 coord, vec2 tex_size)
{
vec2 dx_scaled, dy_scaled;
vec2 coord_scaled = coord * tex_size;
dx_scaled = dFdx(coord_scaled);
dy_scaled = dFdy(coord_scaled);
vec2 dtex = dx_scaled*dx_scaled + dy_scaled*dy_scaled;
float min_delta = max(dtex.x,dtex.y);
float miplevel = max(0.5 * log2(min_delta), 0.0);
return miplevel;
}
|
I believe I tweaked this until it gave me fairly close results to the hardware I was running on, but different hardware can use different approximations. I probably also based it on some existing code I found out in an extension or on opengl.org or something.
| Quote: | | btw all other mipmapLevel calculation functions i've seen multiply the UV coordinates by (virtual)texturesize before passing to dFdx/y... |
Yeah, I do this, but effectively it just multiplies a constant scale, which once you take the log2 becomes an add of a constant, and the code you quoted has a comment about biasing by log2(texturesize), which should have the same effect. I prefer to make it explicit in case the texture size isn't square, though. |
|
| Back to top |
|
 |
skynet
Joined: 01 Sep 2008 Posts: 20
|
| |
Posted: Tue Mar 02, 2010 1:28 pm Post subject: |
 |
|
I have been a bit imprecise in my first statement. Sean is right, I have put the scaling by size of the virtual texture (i.e. 128k or so) into the added bias.
First I was using glTexParameteri and GL_TEXTURE_LOD_BIAS, but I realized that the available range (-15...15) was not enough, so I do it now myself in the shader:
| Code: |
uniform float u_lambdabias;
...
...
lambda += u_lambdabias;
vec3 vt_to_cache = textureLod(t_pagetable, gl_TexCoord[0].xy, lambda).xyz;
|
u_lambdabias is set to log2(virtual_texture_size) + my_lod_bias |
|
| Back to top |
|
 |
julian
Joined: 17 Oct 2008 Posts: 38
|
| |
Posted: Tue Mar 09, 2010 5:29 pm Post subject: |
 |
|
| sean wrote: |
| Code: |
dx_scaled = dFdx(coord_scaled);
dy_scaled = dFdy(coord_scaled);
vec2 dtex = dx_scaled*dx_scaled + dy_scaled*dy_scaled;
|
|
thanks.
i've been told this function is not technically correct since it adds the wrong values (dudx2 + dudy2 instead of dudx2 + dvdx2), and i have been using this one [1].
from the anisotropy spec:
| Quote: |
"Anisotropic texture filtering substantially changes Section 3.8.5.
Previously a single scale factor P was determined based on the
pixel's projection into texture space. Now two scale factors,
Px and Py, are computed.
Px = sqrt(dudx^2 + dvdx^2)
Py = sqrt(dudy^2 + dvdy^2)
Pmax = max(Px,Py)
Pmin = min(Px,Py)
N = min(ceil(Pmax/Pmin),maxAniso)
Lamda' = log2(Pmax/N)
|
so i've modified my function for mipcalculation for anisotropy, i'm not 100% sure it is correct, but it seems to work fine [2].
in any case, as i already have mentioned above, what is weird is that testing the actual mipmap selection as done by the hardware doesn't seem to do anything like the spec. turning on anisotropy doesn't change mip-map selection at all when GL_*_MIPMAP_NEAREST is used, and only very subtle (not like the spec implies) when using GL_*_MIPMAP_LINEAR.
[1]:
| Code: | 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) + mip_bias - readback_reduction_shift;
} |
[2]:
| Code: | float mipmapLevelAnis(vec2 uv, vec2 textureSize)
{
vec2 dx = dFdx(uv * textureSize.x);
vec2 dy = dFdy(uv * textureSize.y);
float Pmax = max(dot(dx, dx), dot(dy, dy));
float Pmin = min(dot(dx, dx), dot(dy, dy));
float N = min(ceil(Pmax/Pmin), max_anisotropy);
return 0.5 * log2(Pmax / N) + mip_bias - readback_reduction_shift;
} |
|
|
| Back to top |
|
 |
sean
Joined: 01 Feb 2005 Posts: 1392 Location: Kirkland WA
|
| |
Posted: Tue Mar 09, 2010 6:00 pm Post subject: |
 |
|
| julian wrote: |
| Code: | vec2 dx = dFdx(uv * textureSize.x);
vec2 dy = dFdy(uv * textureSize.y); |
|
I don't think this bit is right. It seems like it should still be:
| Code: | vec2 dx = dFdx(uv * textureSize);
vec2 dy = dFdy(uv * textureSize); |
Probably your texture is square so it computes the same thing either way.
As to the anisotropy, yeah, i guess that makes sense, and yeah, I'm not surprised hardware doesn't actually compute this stuff accurately. They seem to do a lot of hacks to avoid aniostropic filtering when they can. |
|
| Back to top |
|
 |
julian
Joined: 17 Oct 2008 Posts: 38
|
| |
Posted: Tue Mar 09, 2010 6:55 pm Post subject: |
 |
|
>Probably your texture is square so it computes the same thing either way.
thanks. it currently is (square), but it might not be in the future  |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|