Tag Archives: GLSL

Radial Blur Filter

Radial blur is an effect that is often used in racing games and FPS-games. In racing games it can give the feel of speed and in fps games it’s often used when something important has happened, for example the player has been hit by an enemy. Radial blur is one of many blur filters that can be created in a shader
The image below shows to the left the original image and to the right the radial blurred one.

Radial Blur in the right image. The left image is the original.

The effects work like the following. First render the scene normally to a texture, then draw a full-screen quad with the radial blur shader.

The radial blur shader blurs the image in the direction towards the center of the screen. When considering uv-mapping, the center of the screen is the coordinates 0.5,0.5 because the uv-mapping starts with 0,0 and ends with 1,1. When drawing a fragment, we know it’s current uv-mapping coordinates (this can be the black dot in the image below) since it’s passed down by the vertex shader. We can simply create the direction vector by subtracting the current fragments uv-coordinates from the center of the screen coordinates. This vector will have an distance which can be used when blurring so that the image is more blurred the further it’s away from the middle (like the image above). Of course you can also remove this distance check and do a constant radial blur over the whole screen.

Radial Blur

The GLSL vertex shader:

varying vec2  uv;
// this vertex shader is from AMD RenderMonkey
void main(void)
{
   gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
   gl_Position = sign( gl_Position );
   
   // Texture coordinate for screen aligned (in correct range):
   uv = (vec2( gl_Position.x, - gl_Position.y ) + vec2( 1.0 ) ) / vec2( 2.0 );
}

The GLSL fragment shader:

// This texture should hold the image to blur.
// This can perhaps be a previous rendered scene.
uniform sampler2D tex;
 
// texture coordinates passed from the vertex shader
varying vec2 uv;
 
// some const, tweak for best look
const float sampleDist = 1.0;
const float sampleStrength = 2.2; 
 
void main(void)
{
   // some sample positions
   float samples[10] =
   float[](-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08);
 
    // 0.5,0.5 is the center of the screen
    // so substracting uv from it will result in
    // a vector pointing to the middle of the screen
    vec2 dir = 0.5 - uv; 
 
    // calculate the distance to the center of the screen
    float dist = sqrt(dir.x*dir.x + dir.y*dir.y); 
 
    // normalize the direction (reuse the distance)
    dir = dir/dist; 
 
    // this is the original colour of this fragment
    // using only this would result in a nonblurred version
    vec4 color = texture2D(tex,uv); 
 
    vec4 sum = color;
 
    // take 10 additional blur samples in the direction towards
    // the center of the screen
    for (int i = 0; i < 10; i++)
    {
      sum += texture2D( tex, uv + dir * samples[i] * sampleDist );
    }
 
    // we have taken eleven samples
    sum *= 1.0/11.0;
 
    // weighten the blur effect with the distance to the
    // center of the screen ( further out is blurred more)
    float t = dist * sampleStrength;
    t = clamp( t ,0.0,1.0); //0 &lt;= t &lt;= 1
 
    //Blend the original color with the averaged pixels
    gl_FragColor = mix( color, sum, t );
} 

This shader is mostly inspired and copied from a CG-shader in Ogre3D.
www.ogre3D.org

Percentage Closer Filtering for Shadow Mapping

This is a technique for making softer shadows when doing shadow mapping. It works by filtering the result of the depth comparison. So when comparing a depth, some depths around should also be compared and the result should be averaged. This will give a softer look on the shadow edges.

An example of the the soft shadows when using PCF Shadow Mapping

It can be implemented as simple as this in a the pixel shader:

float result;
result = shadow2DProj(shadowMap,texCoord+offset[0]);
result += shadow2DProj(shadowMap,texCoord+offset[1]);
result += shadow2DProj(shadowMap,texCoord+offset[2]);
result += shadow2DProj(shadowMap,texCoord+offset[3]);
result /= 4.0; // now result will hold the average shading

The samples are often either taken in a grid-based square around the original sample location or randomly scattered around it.

Optimization:

NVIDIA has built in hardware support for doing bilinear interpolation between four samples.

ATI has fetch4 which will fetch four texture samples at the same time.  

The original paper for PCF:
http://graphics.pixar.com/library/ShadowMaps/paper.pdf

A technique that is similar (percentage-closer soft shadows)
http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf

A paper including lot’s of shadow mapping information, including different ways to do PCF (the image in this article is borrowed from this presentation):
http://developer.amd.com/media/gpu_assets/Isidoro-ShadowMapping.pdf

Basic Shadow Mapping

Shadow mapping works in that it checks if a point is visible from the light or not. If a point is visible from the light then it’s obviously not in shadow, otherwise it is. The basic shadow mapping algorithm can be described as short as this:

  1. Render the scene from the lights view and store the depths as shadow map
  2. Render the scene from the camera and compare the depths, if the current fragments depth is greater than the shadow depth then the fragment is in shadow
Shadow mapping example

It’s the implementation of it that is hard.

The two big problem areas with shadow mapping:

  • Hard to select an appropriate bias (epsilon)
  • Hard to get rid of artifacts at shadow edges

Projective texturing ( the method used to transform the fragment depth to the light space (where the shadow map is) for comparision)
http://developer.nvidia.com/object/Projective_Texture_Mapping.html
http://en.wikipedia.org/wiki/Projective_texture_mapping

OpenGL fixed-function pipeline implementation of shadow mapping:
http://www.paulsprojects.net/tutorials/smt/smt.html

A GLSL implementation of shadow mapping (in one of the posts)
http://www.gamedev.net/community/forums/topic.asp?topic_id=316147

Another GLSL shadow mapping shader:
http://sombermoon.com/shadowmappingdoc.html

DirectX9 shadow mapping example with source
http://msdn.microsoft.com/en-us/library/bb147372(VS.85).aspx

Nvidias implementation of shadow mapping with source for both OpenGL and DirectX.
http://developer.nvidia.com/object/hwshadowmap_paper.html

Shadow mapping in XNA
http://www.riemers.net/eng/Tutorials/DirectX/Csharp/Series3/Shadow_mapping.php
http://msdn.microsoft.com/en-us/library/bb975671.aspx

Gaussian Blur Filter Shader

There are different ways to perform blur and this is one of the most common way to do it in a shader. It’s a two step method with first a horizontal blur and then a vertical blur. By splitting the work in two directions (two passes) you can save a lot of computation.

The method can be divided in the following parts:

  1. Render the scene you want to blur to a texture (could be downsampled)
  2. Render a screen aligned quad with the horizontal blur shader to a texture
  3. Render a screen aligned quad with the vertical blur shader to the screen or texture depending on what you want to use it for

The following image shows how the blur works when splitted up in two directions.

Separable blur filter

Here’s the horizontal blur shader.

Vertex Shader (GLSL) . This shader screen align a quad with width 1. Any method to render a screen aligned quad will work. So you’re free to use other shaders.

varying vec2 vTexCoord;
 
// remember that you should draw a screen aligned quad
void main(void)
{
   gl_Position = ftransform();;
  
   // Clean up inaccuracies
   vec2 Pos;
   Pos = sign(gl_Vertex.xy);
 
   gl_Position = vec4(Pos, 0.0, 1.0);
   // Image-space
   vTexCoord = Pos * 0.5 + 0.5;
}

Fragment Shader (GLSL) 

uniform sampler2D RTScene; // the texture with the scene you want to blur
varying vec2 vTexCoord;
 
const float blurSize = 1.0/512.0; // I've chosen this size because this will result in that every step will be one pixel wide if the RTScene texture is of size 512x512
 
void main(void)
{
   vec4 sum = vec4(0.0);
 
   // blur in y (vertical)
   // take nine samples, with the distance blurSize between them
   sum += texture2D(RTScene, vec2(vTexCoord.x - 4.0*blurSize, vTexCoord.y)) * 0.05;
   sum += texture2D(RTScene, vec2(vTexCoord.x - 3.0*blurSize, vTexCoord.y)) * 0.09;
   sum += texture2D(RTScene, vec2(vTexCoord.x - 2.0*blurSize, vTexCoord.y)) * 0.12;
   sum += texture2D(RTScene, vec2(vTexCoord.x - blurSize, vTexCoord.y)) * 0.15;
   sum += texture2D(RTScene, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
   sum += texture2D(RTScene, vec2(vTexCoord.x + blurSize, vTexCoord.y)) * 0.15;
   sum += texture2D(RTScene, vec2(vTexCoord.x + 2.0*blurSize, vTexCoord.y)) * 0.12;
   sum += texture2D(RTScene, vec2(vTexCoord.x + 3.0*blurSize, vTexCoord.y)) * 0.09;
   sum += texture2D(RTScene, vec2(vTexCoord.x + 4.0*blurSize, vTexCoord.y)) * 0.05;
 
   gl_FragColor = sum;
}

And here’s the vertical blur shader.

Vertex Shader (GLSL) (the same as for the blur in horizontal direction)

varying vec2 vTexCoord;
 
// remember that you should draw a screen aligned quad
void main(void)
{
   gl_Position = ftransform();;
  
   // Clean up inaccuracies
   vec2 Pos;
   Pos = sign(gl_Vertex.xy);
 
   gl_Position = vec4(Pos, 0.0, 1.0);
   // Image-space
   vTexCoord = Pos * 0.5 + 0.5;
}

Fragment Shader (GLSL) 

uniform sampler2D RTBlurH; // this should hold the texture rendered by the horizontal blur pass
varying vec2 vTexCoord;
 
const float blurSize = 1.0/512.0;
 
void main(void)
{
   vec4 sum = vec4(0.0);
 
   // blur in y (vertical)
   // take nine samples, with the distance blurSize between them
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 4.0*blurSize)) * 0.05;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 3.0*blurSize)) * 0.09;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 2.0*blurSize)) * 0.12;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - blurSize)) * 0.15;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + blurSize)) * 0.15;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 2.0*blurSize)) * 0.12;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 3.0*blurSize)) * 0.09;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 4.0*blurSize)) * 0.05;
 
   gl_FragColor = sum;
}

And this is a scene without blur.

Scene before bluring

And this is the same scene but with gaussian blur.

Blured Scene

You can tweak the blur radius to change the size of the blur and change the number of samples in each direction.

Cost for separable blur shader : 9+9 = 18 (number of texture samples)
Cost for shader if blured in one pass: 9*9 = 81 (number of texture samples)
So splitting up in two directions saves a lot.

The gaussian weights are calculated accordingly to the gaussian function with standard deviation of 2.7. These calculations were done in the excel document found [2].

Here’s a description of blur shaders and other image processing shaders in DirectX:
[1] http://ati.amd.com/developer/shaderx/ShaderX2_AdvancedImageProcessing.pdf

More info about calculating weights for separable gaussian blur:
[2] http://theinstructionlimit.com/?p=40