Floatingpoint cubemap and filter.

Anyone have a hint about I should implement bilinear filter for FP textures in a fragment programs? I mean, for a normal 2d texture it seems easy, but for a vector?

Here’s a simple Cg function that returns a bilinear filtered sample from a rectangle texture:

float4 texRECT_bilinear(samplerRECT texture, float2 uv)
{
    float2 weight = frac(uv);
   
    float4 bottom = lerp(texRECT(texture, uv),
                         texRECT(texture, uv + float2(1, 0)),
                         weight.x);

    float4 top = lerp(texRECT(texture, uv + float2(0, 1)), 
                      texRECT(texture, uv + float2(1, 1)), 
                      weight.x);

    return lerp(bottom, top, weight.y);
}

[This message has been edited by jra101 (edited 09-04-2003).]

Thanks, but that stlil just x,y ( easy to find neighbour pixels), I need one for Cubemaps, where you have x,y,z as incomming texture coords.

Yes, but if you project the coordinate to the appropriate face (what the hardware does), then you can handle it the 2D way.

cass,
that won’t handle filtering across cube map face edges. I take it the fixed point format cubemap filters can do this?

And its not like i have 6 different textures do that i can select a single texture. i still need to access the cube texture via 3 coords, and 2 of those coords should be changed, without any ‘if’ i hope

Originally posted by zeckensack:
cass,
that won’t handle filtering across cube map face edges. I take it the fixed point format cubemap filters can do this?

I don’t think so. 'cause when you don’t ask for clamp_to_edge you can get visual artifacts, which wouldn’t happen if it was filtered across edges.

thats a good point.

Cass : could you hint me with some small example code?

Doing bilinear filtering on a cubemap will be very slow (I had code that did this a while ago, but I don’t know where it is, anymore). Basically, the algorithm is (note that this doesn’t handle cubeseams or floating point special numbers, and there is no epsilon handling, so YMMV):

float3 cubeVec = i.cubeVec;
float3 cubeVec_abs = abs(cubeVec);

float largest_comp = max(cubeVec_abs.z, (max(cubeVec_abs.x, cubeVec_abs.y));
cubeVec /= largest_comp;

float face_selector = dot(float3(cubeVec==1.0), float3(0, 2, 4)) + dot(float3(cubeVec==-1.0), float3(1,3,5));

This should give you the cube face (0=posX, 2=posY, 4=posZ, 1=negX, 3=negY, 5=negZ) and the projected texture coordinates. You will need to use some if/else statements to correctly choose the 2D s,t pair from the 3D vector, since this varies based on the cube face. Once you get the 2D s,t pair, you would add in bilinear filtering like you would for a standard 2D texture, then reproject it back into cube coordinates using another if/else statement.

You should be able to find what the right code for the if/else statements is from normalization cube map generation code.

It’s ugly, but it should work (I haven’t tested this code).

float cube_face = dot(face_selector(2, 4, 6

Instead of ifs/elses you could use 2 precomputed small unfiltered 1x1 cube map LUTs, containing coefficients of matrix which “swizzles” components of original texcoord to get the projected texcoords + magnitude

float4 ss_and_tt = texCUBE(/*LUT#1*/, texcoord) // signed RGBA
float4 mag_and_f = texCUBE(/*LUT#2*/, texcoord) // just RGBA

// each component of ss_and_tt is one of 0, +1, -1
// each component of mag_and_f.xyz is 0 or 1 (only one is nonzero)
// all according to table in ARB_tex_cube_map spec

float s = dot(ss_and_tt.xy, texcoord.xz)  
float t = dot(ss_and_tt.zw, texcoord.yz)
float mag = dot(mag_and_f.xyz, abs(texcoord))
float face = mag_and_f.w * 255   // (or something)

Don’t know if that would be faster, just an idea.

BTW, adding support for redundant border + across-edge filtering would be free, i think…

[This message has been edited by MZ (edited 09-05-2003).]

correction:
I forgot you can’t encode 0 exactly in signed RGBA format, and the trick relys on having 100% exact 0, +1, -1 values in ss_and_tt. To fix this, you would have to store in texel value slightly higher then 0 instead of just 0 (like 0.5, for example) and apply floor() after getting the sample.