[Shader Library] Night Vision Post Processing Filter (GLSL)

Shader Library - Night Vision PostFX

This shader officially starts Geeks3D’s shader library. The Shader Library is the place for all shaders I use in GeeXLab demos. Most of these shaders can be easily used in other softwares like Blender Game Engine for example.

This night vision shader is based on this algorithm with some modifications. I added a simple luminance analysis in order to amplify parts with low luminance.

You can download the demo to see the shader in real time:

Unzip the demo somewhere and run Night_Vision_Demo.exe. That’s all.

You need Python 2.6.3 (2.6.2 is also ok) to run the demo. If you don’t have yet Python, get the Windows installer HERE.

This demo uses GeeXLab. The source code of the night vision filter can be found in the PostFX_Night_Vision_Lib.xml file. The main source code (that is loaded first) is Night_Vision_Demo.xml.

Shader desription

Language: OpenGL 2 – GLSL

Type: Post processing filter.


  • sceneBuffer (sampler2D): the scene buffer.
  • noiseTex (sampler2D): noise texture to add some artifacts to the night vision device. The noise texture comes from this free noise texture pack.
  • maskTex (sampler2D): binocular mask of the night vision device.
  • elapsedTime (float): elapsed time in seconds.
  • luminanceThreshold (float): allows to change the luminance threshold. Default value: 0.2
  • colorAmplification (float): amplification factor for pixels with low luminance. Default value: 4.0
  • effectCoverage (float): effect screen coverage – only for demo needs! Default value: 0.5 (half screen)

Ouputs: color buffer

Shader code:

void main()
  gl_Position = ftransform();		
  gl_TexCoord[0] = gl_MultiTexCoord0;
uniform sampler2D sceneBuffer;
uniform sampler2D noiseTex; 
uniform sampler2D maskTex; 
uniform float elapsedTime; // seconds
uniform float luminanceThreshold; // 0.2
uniform float colorAmplification; // 4.0
uniform float effectCoverage; // 0.5
void main ()
  vec4 finalColor;
  // Set effectCoverage to 1.0 for normal use.  
  if (gl_TexCoord[0].x < effectCoverage) 
    vec2 uv;           
    uv.x = 0.4*sin(elapsedTime*50.0);                                 
    uv.y = 0.4*cos(elapsedTime*50.0);                                 
    float m = texture2D(maskTex, gl_TexCoord[0].st).r;
    vec3 n = texture2D(noiseTex, 
                 (gl_TexCoord[0].st*3.5) + uv).rgb;
    vec3 c = texture2D(sceneBuffer, gl_TexCoord[0].st 
                               + (n.xy*0.005)).rgb;
    float lum = dot(vec3(0.30, 0.59, 0.11), c);
    if (lum < luminanceThreshold)
      c *= colorAmplification; 
    vec3 visionColor = vec3(0.1, 0.95, 0.2);
    finalColor.rgb = (c + (n*0.2)) * visionColor * m;
    finalColor = texture2D(sceneBuffer, 
  gl_FragColor.rgb = finalColor.rgb;
  gl_FragColor.a = 1.0;

Before testing on ATI Radeon, the shader contained this line:

float lum = dot(vec4(0.30, 0.59, 0.11, 0.0), c);

where c is a vec3. NVIDIA GLSL compiler (based on Cg) accepts this instruction.
But Radeon GLSL compiler is strict and doesn't accept this line because it can't compile:

float lum = dot(vec4, vec3);

Little conclusion: always test your shaders on ATI and NVIDIA boards...

Related links and references: