Here is a screenshot featuring cascaded image filters (fully defined inside a Cg fragment shader). Each filter can have any number of textures as input (typically one) and its output is done on one texture (but with some work, maybe I could get multiple output if needed). The only filter I have implemented so far is a generic convolution filter (with any possible kernel size and kernel values).

Image 1.png{.imagelink}

This seems to run pretty fast (25 fps with 5 filters applied at each frame) on my GeForce FX Go5200 (poor graphic card of my PowerBook 12″, with 64 MB VRAM).

Keep in mind that this framerate is achieved on a system, Mac OS X, that already makes an extensive use of the GPU !

Here is a sample Cg fragment shader that performs the sharpen filter above :

float4 ApplyKernel(
		const float kernel[][],
		const uniform samplerRECT texture,
		const float2 sizeTexture,
		const float2 position
) {
	float4 result = float4(0.0,0.0,0.0,0.0);
	const float2 kernelCenter = (float2(kernel.length,kernel[0].length)-float2(1,1))/2.0;
	for (int i=0; i < kernel.length; i++) {
		const float newPositionX = position.x + i - kernelCenter.x;
		for (int j=0; j < kernel[0].length; j++) {
			const float2 newPosition =
				float2(
					newPositionX,
					position.y + j - kernelCenter.y
				)
			;
			result +=
				texRECT(
					texture,
					//(position+float2(i,j)-kernelCenter)
					newPosition
				) * kernel[i][j]
			;
		}
	}
	return result;
}
float4 ApplyLaplacian(
		const uniform samplerRECT texture,
		const float2 sizeTexture,
		const float2 position
) {
	const float norm = 6.8;
	const float kernel[3][3] = {
		{ -0.7/norm, -1.0/norm, -0.7/norm },
		{ -1.0/norm, 6.8/norm, -1.0/norm },
		{ -0.7/norm, -1.0/norm, -0.7/norm }
	};
	return ApplyKernel(
		kernel,
		//float2(1,1),
		texture,
		sizeTexture,
		position
	);
}
float4 ApplyGaussianBlur(
		const uniform samplerRECT texture,
		const float2 sizeTexture,
		const float2 position
) {
	const float norm = 16.0;
	const float kernel[3][3] = {
		{ 1.0/norm, 2.0/norm, 1.0/norm },
		{ 2.0/norm, 4.0/norm, 2.0/norm },
		{ 1.0/norm, 2.0/norm, 1.0/norm }
	};
	return ApplyKernel(
		kernel,
		//float2(1,1),
		texture,
		sizeTexture,
		position
	);
}
void fragmentMain(
	in float2 texCoordIn      : TEXCOORD0,
	out float4 colorOut : COLOR,

	uniform float intParameter1,
	uniform float intParameter2,

	uniform samplerRECT texture1,
	uniform float2 sizeTexture1,

	//uniform samplerRECT texture2,
	uniform float2 sizeTexture2
) {

	float4 original = texRECT(texture1, texCoordIn);

	float4 result =
		ApplyLaplacian
		//ApplyGaussianBlur
			(texture1, sizeTexture1, texCoordIn)
		;

	const float increaseFactor = intParameter1;
	const float originalFactor = intParameter2;
	colorOut = float4((
		original * originalFactor +
		increaseFactor * result
	).rgb,1.0);
}