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;
	sample0 = shadow2D(texture,vec3(texcoord.x,texcoord.y,texcoord.z)).x;
	sample1 = shadow2D(texture,vec3(texcoord.x+psx,texcoord.y,texcoord.z)).x;
	sample3 = shadow2D(texture,vec3(texcoord.x,texcoord.y,texcoord.z)).x;	
	sample2 = shadow2D(texture,vec3(texcoord.x,texcoord.y+psy,texcoord.z)).x;
	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;

Thats how I do it in my variance-shadowmap shader:
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.