Sign in to follow this  
  • entries
  • comments
  • views

Reflection Shader

Sign in to follow this  


Since someone asked about it, I thought I'd post up the details of the reflection shader from earlier (the ice scene).

Heres the actual GLSL fragment shader source:

uniform sampler2D reflectMap; // Scene is held in this texture

const int numSamples = 16;
const float divisor = 1.0 / float(numSamples);

vec2 samples[numSamples];

void main()
// Our generated poisson disc sample offsets
samples[0] = vec2(0.007937789, 0.73124397);
samples[1] = vec2(-0.10177308, -0.6509396);
samples[2] = vec2(-0.9906806, -0.63400936);
samples[3] = vec2(-0.5583586, -0.3614012);
samples[4] = vec2(0.7163085, 0.22836149);
samples[5] = vec2(-0.65210974, 0.37117887);
samples[6] = vec2(-0.12714535, 0.112056136);
samples[7] = vec2(0.48898065, -0.66669613);
samples[8] = vec2(-0.9744036, 0.9155904);
samples[9] = vec2(0.9274436, -0.9896486);
samples[10] = vec2(0.9782181, 0.90990245);
samples[11] = vec2(0.96427417, -0.25506377);
samples[12] = vec2(-0.5021933, -0.9712455);
samples[13] = vec2(0.3091557, -0.17652994);
samples[14] = vec2(0.4665941, 0.96454906);
samples[15] = vec2(-0.461774, 0.9360856);

// Blur weight comes from vertex colour
float weight = gl_Color.r;

// Blur spread amount
float spread = min(weight*2.0, 0.5);
spread *= 0.010;

// -- Distortion --

// Grab the base texture coord
vec2 baseCoord = gl_TexCoord[0].xy;

// Distort the texture coord based on a couple of sine waves
float offsetX = sin( weight * 80.0 );
offsetX *= 0.0015;

float offsetY = sin( baseCoord.x * 16.0 );
offsetY *= 0.009;

baseCoord += vec2(offsetX, offsetY);

// -- Blur --

// We'll accumulate the weighted samples here
vec4 cumulative = vec4(0, 0, 0, 0);

// Sample the texture at each sample point and accumulate
for (int i=0; i {
cumulative += texture2D(reflectMap, baseCoord + (samples * spread) ) * divisor;

// -- Greyscale and output --

// Generate a greyscale colour of the final blur
float grey = cumulative.x*0.3 + cumulative.y *0.3 + cumulative.z*0.3;
float inv = 1.0 - weight;

vec4 greyCol = vec4(grey, grey, grey, 1.0);

// Final frag colour is the weighted avarage of the blur and the greyscale version
gl_FragColor = (cumulative * inv) + (greyCol * weight);

Theres basically three sections to the shader - the sine wave distortion, the blur, and the greyscale fade in. The distortion is pretty hacky, but it breaks it up from being a perfect reflection.

The blur uses the growable poission disc method, where the disc is bigger based on the blur weight. The weight comes in via the vertex colour's red channel. It gets a bit artifact-y on big blurs, but the whole reflection has a separate texture blended over the top afterwards which hides a lot. The other problem my blur originally had was my sample points were generated in a square rather than a circle.

The greyscale is a simple avarage of the rgb value from the blur. A good improvement would be to use proper weights rather than a fixed third for each, but I havn't got around to looking them up yet. The same weight which controls the blur is used for the greyscale fade in.

This probably isn't totally optimal - I'm sure theres a better way to setup the array of sample points, but I can't figure out how to do that in GLSL. It might be a good idea to remove the two sin() calls too, either by putting it in a texture or subdividing into lots of polys and doing it per-vertex. I'm also biasing the weight in the shader where I'd be better of doing that per-vertex too. On slower systems a version with less samples for the blur would help a lot.

Still, even without those possible optimisations it runs pretty damn fast, and gives nice results too. [grin]
Sign in to follow this  


Recommended Comments

There's a page on the colour weightings (and other greyscale methods) used by GIMP here.

I haven't done any shader work so maybe this is obvious, but is there a reason you multiply by "divisor" within the for loop instead of doing it once afterwards? If this does work, does it have any noticable effect on the shader's speed?

Share this comment

Link to comment
neato :)

i'm working on a space-type game, and explosions are the last bit i need to produce.. would you be able to post the fragment code from your ripple distortion? i've just recently come to terms with the sine function, and my mind still doesn't wrap around it quick enough to figure out a distortion effect yet.

is a youtube link to a video on my project if you're interested.

Share this comment

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now