6

The refpages say "Returns the weighted average of the four texture elements that are closest to the specified texture coordinates." How exactly are they weighted? And what about 3D textures, does it still only use 4 texels for interpolation or more?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Wingblade
  • 9,585
  • 10
  • 35
  • 48

2 Answers2

2

in 2D textures are 4 samples used which means bi-linear interpolation so 3x linear interpolation. The weight is the normalized distance of target texel to its 4 neighbors.

So for example you want the texel at

(s,t)=(0.21,0.32)

but the texture nearby texels has coordinates:

(s0,t0)=(0.20,0.30)
(s0,t1)=(0.20,0.35) 
(s1,t0)=(0.25,0.30) 
(s1,t1)=(0.25,0.35)

the weights are:

ws = (s-s0)/(s1-s0) = 0.2
wt = (t-t0)/(t1-t0) = 0.4

so linear interpolate textels at s direction

c0 = texture(s0,t0) + (texture(s1,t0)-texture(s0,t0))*ws
c1 = texture(s0,t1) + (texture(s1,t1)-texture(s0,t1))*ws

and finally in t direction:

c = c0 + (c1-c0)*wt

where texture(s,t) returns texel color at s,t while the coordinate corresponds to exact texel and c is the final interpolated texel color.

In reality the s,t coordinates are multiplied by the texture resolution (xs,ys) which converts them to texel units. after that s-s0 and t-t0 is already normalized so no need to divide by s1-s0 and t1-t0 as they are booth equal to one. so:

s=s*xs; s0=floor(s); s1=s0+1; ws=s-s0;
t=t*ys; t0=floor(t); t1=t0+1; wt=t-t0;
c0 = texture(s0,t0) + (texture(s1,t0)-texture(s0,t0))*ws;
c1 = texture(s0,t1) + (texture(s1,t1)-texture(s0,t1))*ws;
c = c0 + (c1-c0)*wt;

I never used 3D textures before but in such case it use 8 textels and it is called tri-linear interpolation which is 2x bi-linear interpolation simply take 2 nearest textures and compute each with bi-linear interpolation and the just compute the final texel by linear interpolation based on the u coordinate in the exact same way ... so

u=u*zs; u0=floor(u); u1=u0+1; wu=u-u0;
c = cu0 + (cu1-cu0)*wu;

where zs is count of textures, cu0 is result of bi-linear interpolation in texture at u0 and cu1 at u1. This same principle is used also for mipmaps...

All the coordinates may have been offseted by 0.5 texel and also the resolution multiplication can be done with xs-1 instead of xs based on your clamp settings ...

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • This answer has a mistake. If adjacent texels are 0.05 apart (as in example texel s coordinates 0.20 and 0.25) then the nearby texels have coordinates that are *halfway between* integer multiplies of the texel size. So the "nearby texels" should have coordinates (0.175,0.275), (0.175,0.325), (0.225,0.275) and (0.225,0.325). – Adam Gawne-Cain May 12 '21 at 05:41
  • @AdamGawne-Cain IIRC that depends on texture settings ... if you use `GL_CLAMP_TO_EDGE` like anyone these days then the align is not on center of textel but on the edge ... so the coordinate goes in range `<0,1>` instead of `<0+hp,1-hp>` where hp is half of texel size. What you describe is true for `GL_CLAMP` which was a thing ages ago before the `GL_CLAMP_TO_EDGE` extention ... that is what the last sentence was all about btw... – Spektre May 12 '21 at 05:54
  • Hi Spektre: maybe your "All the coordinates may have been offset by 0.5 texel" covers you, but the OP asked for the *exact* formula, so an answer with "may have been" is incomplete. Kronos specifies GL_CLAMP_TO_EDGE clamps s and t into range [1/2N, 1-1/2N]. – Adam Gawne-Cain May 12 '21 at 08:00
  • @AdamGawne-Cain You can edit in / add offseted version if you want or post as another answer ... I usually add such things to not my post as `[Added by Spektre]` or `[Edit by Spektre]` paragraph so its clear what part is added by someone else that original post author ... while leaving original information untouched – Spektre May 12 '21 at 08:05
1

As well as the bilinear interpolation outlined in Spektre's answer, you should be aware of the precision of GL_LINEAR interpolation. Many GPUs (e.g. Nvidia, AMD) do the interpolation using fixed point arithmetic with only ~255 distinct values between the R,G,B,A values in the texture.

For example, here is pseudo code showing how GPUs might do the interpolation:

float interpolate_red(float red0, float red1, float f) {
    int g = (int)(f*256)
    return (red0*(256-g) + red1*g)/256;
}

If your texture is for coloring and contains GL_UNSIGNED_BYTE values then it is probably OK for you. But if your texture is a lookup table for some other calculation and it contains GL_UNSIGNED_SHORT or GL_FLOAT values then this loss of precision could be a problem for you. In which case you should make your lookup table bigger with in-between values calculated with (float) or (double) precision.

Adam Gawne-Cain
  • 1,347
  • 14
  • 14
  • No, GPU's don't do that. What they actually do is quantize the _sample position_ to a fixed point raster with 8 bit of fractional precision (your `f` here) - so yes, you can still get only 256 different values for mixing neighboring texels, but within that, full float precision is available for the actual mixing. – derhass May 13 '21 at 16:42