# Anybody have a bilinear sample function?

For ATI cards (which lack PCF filter). It doesn’t work right, though.

``````vec4 shadow2D4(in sampler2DShadow texture, in vec3 texcoord, in float width, in float height) {
float psx = 1.0 / width * 1.0;
float psy = 1.0 / height * 1.0;
float sample0;
float sample1;
float sample2;
float sample3;
float hweight=(texcoord.x-(int(texcoord.x/psx)*psx))/psx;
float vweight=(texcoord.y-(int(texcoord.y/psy)*psy))/psy;
float hsample = sample1*hweight + sample0*(1.0-hweight);
float vsample = sample2*vweight + sample3*(1.0-vweight);
return vec4((hsample+vsample)/2.0);
}
``````

You sample and filter wrong.

Sample the positions (x,y), (x+dx, y), (x,y+dy), (x+dx,y+dy). These are essentially the corners of a square.

First you interpolate across the x axis according to the xweight:

x1 = (1 - xweight) * sample(x,y) + xweight * sample(x+dx, y);
x2 = (1 - xweight) * sample(x,y+dy) + xweight * sample(x+dx, y+dy);

Then you interpolate these new values along the y axis, using yweight:

final = (1 - yweight) * x1 + yweight * x2;

I cant see any noticable differences between the hw-filtered and
manually-filtered version, so I hope its correct.
The code aims to be a 1:1 translation of the linear filtering described in the spec.

``````
float alpha = fract(texcoords.x * SHADOWMAP_SIZE - 0.5);
float beta = fract(texcoords.y * SHADOWMAP_SIZE - 0.5);

vec2 st_00 = texcoords + vec2(-0.5, -0.5) / SHADOWMAP_SIZE;
vec2 st_10 = texcoords + vec2(0.5, -0.5) / SHADOWMAP_SIZE;
vec2 st_01 = texcoords + vec2(-0.5, 0.5) / SHADOWMAP_SIZE;
vec2 st_11 = texcoords + vec2(0.5, 0.5) / SHADOWMAP_SIZE;

data = (1 - alpha)*(1 - beta) * texture2D(u_light_shadow[i], st_00)
+ alpha * (1 - beta) * texture2D(u_light_shadow[i], st_10)
+ (1 - alpha)*beta * texture2D(u_light_shadow[i], st_01)
+ alpha * beta * texture2D(u_light_shadow[i], st_11);

``````

Thanks! I took your code and made a nice little function. Now my shadowmaps look exactly the same on ATI and NVidia cards, with no need for rotated grids or any such nonsense:

``````vec4 shadow2D4(in sampler2DShadow texture, in vec3 texcoord, in float width, in float height) {
float alpha = fract(texcoord.x * width - 0.5);
float beta = fract(texcoord.y * height - 0.5);

vec3 st_00 = vec3(texcoord.x-0.5/width,texcoord.y-0.5/height,texcoord.z);
vec3 st_10 = vec3(texcoord.x+0.5/width,texcoord.y-0.5/height,texcoord.z);
vec3 st_01 = vec3(texcoord.x-0.5/width,texcoord.y+0.5/height,texcoord.z);
vec3 st_11 = vec3(texcoord.x+0.5/width,texcoord.y+0.5/height,texcoord.z);

return (1.0 - alpha)*(1.0 - beta) * shadow2D(texture, st_00)
+ alpha * (1.0 - beta) * shadow2D(texture, st_10)
+ (1.0 - alpha)*beta * shadow2D(texture, st_01)
+ alpha * beta * shadow2D(texture, st_11);
}
``````

How’s the performance-penalty?
I wasn’t able to measure it on cards with comparable speed.

4 nearest-filter texture lookups.

I really don’t care what the performance penalty is. If it means an ATI user has to have “medium” quality instead of “high”, that is preferable to me having to pass a random noise texture in or do some other crazy workaround.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.