Optimizing gaussian blur with linear filtering

There's a few (here, and here) gaussian blur tutorials out there suggesting that you can essentially cut the number of texture lookups in a gaussian blur shader in half by exploiting the GPU's hardware linear filtering.

Essentially turning this:

Into this:

It sounds good in theory, but after attempting to implement this, I've come to the conclusion that it can't be done correctly, and I'd like someone to prove me wrong :)

My beef lies with the weights. Take the far left 2 texels in the first image. Their overall contribution to the shader equals:

t1 * w1 + t2 * w2


In the image, their weights 1 and 4, respectively. Now let us assign abitrary color values to the texels... 10 and 20. This gives their overall contribution, according to the formula, of 90. Great.

Now let us try to work this out with linear filtering.

What we want is essentially a new sampling location and a new weight, such that the value sampled at this new location, multiplied by the new weight, equals our original formula.

((t1 - t2 * new_offset) + t2) * w3 = t1 * w1 + t2 * w2


Let's make it easy and choose a sampling location halfway between texels 1 and 2, so that the value sampled is 15 (((10 - 20) * .5) + 20). So the new weight MUST be 6, so that 15 * 6 = 90. Ok great.

Now what happens when you change the values...instead of 10 and 20, you use 10 and 30. With the first way, you get 10 * 1 + 30 * 4 = 130. But with the linear sampling way, you get a linear sampled value of 20 ((10 + 30) / 2), times the weight of 6...120! It doesn't match!

Can anyone shed any light on why the linear sampling optimization is thought to work, and what I'm misunderstanding about it? I would be more than thrilled to be wrong about this :)

I don't know why are you choosing 0.5 as offset. The formula is (I'll use o instead of new_offset):

t1*w1 + t2*w2 = ( (t1-t2)*o + t2 )*w3
t1*w1 + t2*w2 = t1*o*w3 - t2*o*w3 + t2*w3
t1*w1 + t2*w2 = t1*(o*w3) + t2*(w3 - o*w3)


Form that we see this:

w1 = o*w3
w2 = w3 - o*w3


From first equation lets get o:

o = w1/w3


And put it into second equation:

w2 = w3 - (w1/w3)*w3 = w3 - w1


And you can calculate w3:

w3 = w2 + w1 (in your case w3 = 4+1 = 5)


And now you can get offset:

o = w1/w3 = w1/(w2+w1) (in your case o = 1/5)


Let's try with t1=10 and t2=20:

t1*w1 + t2*w2 = 10*1 + 20*4 = 90
( (t1-t2)*o + t2 )*w3 = (-10*0.2 + 20)*5 = (-2 + 20)*5 = 90


Great! Let's try with t1=10 and t2=30:

t1*w1 + t2*w2 = 10*1 + 30*4 = 130
( (t1-t2)*o + t2 )*w3 = (-20*0.2 + 30)*5 = (-4 + 30)*5 = 130


Excellent. It works. Right?

• My mind is blown, but it works. I will need to read over this a few times...thanks! – amoffat Apr 16 '12 at 16:37
• btw, there is a small typo... "0.25" should be "0.2". Other than that, it looks really good. – amoffat Apr 16 '12 at 16:40
• You are right. 0.25 should be 0.2 = 1/5 :) – Mārtiņš Možeiko Apr 16 '12 at 17:27

The weight of the new sample must be the sum of the weights of the two samples that you're replacing. The weights are not determined in isolation: if you vary from the correct value, these two texels will either contribute more or less than they should to the total blur. In other words:

w3 = w1 + w2


The only degree of freedom that you have is the actual offset of the new sample, which you cannot simply pick arbitrarily. If your equation looks like this:

(t1 * offset + t2 * (1 - offset)) * w3 = t1 * w1 + t2 * w2


(i.e. an offset of 1 is centered on t1 and an offset of 0 is centered on t2; I think this is the opposite of what you're using), then your offset works out to be this:

offset = (w1 + w2) / w1