OpenGL screen space motion blur

I am implementing motion blur based on the approach outlined in “Stupid OpenGL tricks”.I also got the demo from NVidia OpenGL SDK.It is implemented using Cg while I am using GLSL.Needless to say it doesn’t work for me after conversion,otherwise I wouldn’t ask here for help.Also I am not sure I completely understand the logic of their implementation.

First here is Cg vertex shader:



// Motion blur vertex shader
// sgg 10/2002


struct a2v {
  float4 coord   : POSITION;
//  float4 prevCoord;
  float3 normal  : NORMAL;
  float2 texture : TEXCOORD0;
};


struct v2f {
  float4 hpos     : POSITION;
  float3 velocity : TEXCOORD0;
  float3 col0     : COLOR0;
};


v2f main(a2v In,
         uniform float4x4 modelView,
         uniform float4x4 prevModelView,
         uniform float4x4 modelViewProj,
         uniform float4x4 prevModelViewProj,
         uniform float3   halfWindowSize,
         uniform float    blurScale = 1.0
         )
{
  v2f Out;


  // transform previous and current position to eye space
  float4 P = mul(modelView, In.coord);
  float4 Pprev = mul(prevModelView, In.coord);
//  float4 Pprev = mul(prevModelView, In.prevCoord);
  // transform normal to eye space
  float3 N = mul((float3x3) modelView, In.normal);


  // calculate eye space motion vector
  float3 motionVector = P.xyz - Pprev.xyz;


  // calculate window space motion vector
  P = mul(modelViewProj, In.coord);
//  Pprev = mul(prevModelViewProj, In.prevCoord);
  Pprev = mul(prevModelViewProj, In.coord);


  Pprev = lerp(P, Pprev, blurScale);


  // choose previous or current position based on dot product between motion vector and normal
  float flag = dot(motionVector, N) > 0;
  float4 Pstretch = flag ? P : Pprev;
  Out.hpos = Pstretch;


  // do divide by W -> NDC coordinates
  P.xyz = P.xyz / P.w;
  Pprev.xyz = Pprev.xyz / Pprev.w;
  Pstretch.xyz = Pstretch.xyz / Pstretch.w;


  // calculate window space velocity
  float3 dP = (P.xyz - Pprev.xyz) * halfWindowSize.xyz;


  Out.velocity = dP;
  Out.col0.xy = 0.5 + (dP.xy * 0.005);


  return Out;
}




And here is my GLSL conversion:





//=========  INS   ==================//


layout(location = 0)  in vec4 position;
layout(location = 1)  in vec2 uvs;
layout(location = 2)  in vec3 normal;




//=========   UNIFORMS   ============//
 uniform mat4 MODEL_VIEW_MATRIX;
 uniform mat4 prevModelView;
 uniform mat4 PROJ;
 uniform float blurScale;
 uniform vec3 halfWindowSize;


//=======  OUTS  ===================//


  out vec3 out_velocity;
  out vec4 outCol;
out gl_PerVertex
{
    vec4 gl_Position;
};



void main(void){

  // transform previous and current position to eye space:
    vec4 P =  MODEL_VIEW_MATRIX      *  position;//  
    vec4 Pprev = prevModelView         * position;


    // transform normal to eye space:
    vec3 N = mat3(MODEL_VIEW_MATRIX) * normal;


    // calculate eye space motion vector
    vec3 motionVector = P.xyz - Pprev.xyz;


    // calculate window space motion vector
   P =       PROJ *   MODEL_VIEW_MATRIX *   position;//  
   Pprev = PROJ * prevModelView       * position;


     Pprev = mix( Pprev,P, blurScale);


     // choose previous or current position based on dot product between motion vector and normal
    
     bool flag = dot(motionVector, N) > 0;
     vec4 Pstretch = flag ? P : Pprev;
     gl_Position =  position;
     outCol = Pstretch;
     // do divide by W -> NDC coordinates
  P.xyz = P.xyz / P.w;
  Pprev.xyz = Pprev.xyz / Pprev.w;
  Pstretch.xyz = Pstretch.xyz / Pstretch.w;




  // calculate window space velocity
  vec3 dP = (P.xyz - Pprev.xyz) * halfWindowSize.xyz  ;

 out_velocity = dP;


I have 2 problems with the vertex shader.

  1. In the source code of the original example both modelView matrix and modelViewProjection matrix uniforms are identity matrices.I don’t get it why? To me it seem that in such a case,because the matrices of the
    previous frame are no identity,the velocity vector build would be wrong…
  2. In CG vertex shader they fill varying “Out.hpos” with Pstretch which is used again after several lines to get transformed into 3D space…Why?

Now for the fragment shader:

Here is the original Cg fragment:



struct v2f {
  float4 wpos     : WPOS;
  half3  velocity : TEXCOORD0;
};


half4 main(v2f In,
           uniform samplerRECT sceneTex,
           uniform samplerRECT velTex,
           uniform half blurScale
           ) : COLOR
{
  const float samples = 16;


  half2 wpos = In.wpos.xy;
  half2 velocity = In.velocity.xy * blurScale;           // read velocity from texture coordinate
//  half2 velocity = texRECT(velTex, wpos) * blurScale;  // read velocity from texture


  // sample into scene texture along motion vector
  const fixed w = 1.0 / samples;  // weight
  fixed4 a = 0;
  for(float i=0; i<samples; i+=1) {
    half t = i / (samples-1);
    a = a + texRECT(sceneTex, wpos + velocity*t) * w;
  }


  return a;
}



And this is my conversion :



layout(binding=0) uniform sampler2D COLOR_MAP_0;
uniform float samples;
uniform float blurScale;
uniform vec3 halfWindowSize;
// ==========  INS   ============//
in vec3 out_velocity;
out vec4 colorOut;
 in vec4 outCol;
void main(void){




                            float w = 1.0 / samples;     
                vec4 a=vec4(0.);
                vec2 velocity = out_velocity.xy * blurScale;
                ivec2 tsize = textureSize(COLOR_MAP_0, 0);
                          vec2 screntc = gl_FragCoord.xy * (1.0 / vec2(tsize));
            
                for(int i= 0; i<samples; i++) 
                {
                 
                        float t = i / (samples-1);
                    a =  a + texture(COLOR_MAP_0, outCol.xy + velocity * t ) * w;
         
                }
        
                    colorOut = a; 


}





What I fail to figure out here is how to convert

this method:

" a = a + texRECT(sceneTex, wpos + velocity*t) * w; "

to work with GLSL texture.I am trying like this :

“a = a + texture(COLOR_MAP_0, outCol.xy + velocity * t ) * w;”

But texRECT uses al 4 components of wpos while GLSL texture() has no such an option.

So how do I go about it?

Currently this motion blur technique is the only one which is relatively clear to me and which operates both on camera and objects but I will also be happy to get reference to other (may be better) approaches.

Thanks.

  1. You are setting gl_Position = position in your vertex shader, which means you aren’t applying modelView/modelViewProjection to the vertices + whatever you set them to will have no effect. As far as I can tell, you should be setting gl_Position = PStretch and outCol = 0.5 + (dP.xy * 0.005).
  2. The result of the calculation Pstretch.xyz = Pstretch.xyz / Pstretch.w isn’t used, so can be safely commented out.

You can cast to a 2d vector with vec2(velocity * t).

And what about modelview and modelviewprojection matrices?Do they have to be identity ?

You can cast to a 2d vector with vec2(velocity * t).

What do you mean?This?

a = a + texture(COLOR_MAP_0, outCol.xy + velocity * t ) * w;

It doesn’t work.

The textureProj() function will convert from homogeneous coordinates.

According to http://http.developer.nvidia.com/Cg/texRECT.html texRect accept 2 components too.

A more exact port of the original code would be to use samplerRect (texcoords from 0…width, 0…height) instead of sampler2D (texcoords from 0…1, 0…1), but as long as you’ve adjusted for this correctly it should be okay.

Looking at the code closer, velocity is a vec2 already so the vec2() cast wouldn’t be needed.

In which way doesn’t the line work, have you include a #version 330 directive (or alternative version) to target the correct GLSL version, otherwise you will need to use texture2D() instead of texture().

Version is not a problem…I use 420 … The effects just doesn’t work as it shows in my sample code.The UV is screwed.