Motion Blur and previous viewProjection Matrix

Problems building or running the engine, queries about how to use features etc.
Crashy
Google Summer of Code Student
Google Summer of Code Student
Posts: 1005
Joined: Wed Jan 08, 2003 9:15 pm
Location: Lyon, France
x 49
Contact:

Re: Motion Blur and previous viewProjection Matrix

Post by Crashy »

You're welcome :)
Follow la Moustache on Twitter or on Facebook
Image
User avatar
lordsme
Gremlin
Posts: 167
Joined: Sun Mar 11, 2007 1:11 pm
Location: Turin, Italy
x 10
Contact:

Re: Motion Blur and previous viewProjection Matrix

Post by lordsme »

razi wrote:It works :) , too bad that I still see a little choppiness every now and then, Ill play with the values. Thank you.
Hi razi, I'm going to implement the same effect, would you post some code from your final working version?
Thank you!
My Portfolio
http://davidedigiannantonio.weebly.com

MESH - Mise-en-scene Helper
http://www.mesh-project.org/

ASALab @ Virtual Reality & Multimedia Park
http://www.vrmmp.it
User avatar
lordsme
Gremlin
Posts: 167
Joined: Sun Mar 11, 2007 1:11 pm
Location: Turin, Italy
x 10
Contact:

Re: Motion Blur and previous viewProjection Matrix

Post by lordsme »

I found a way to make this MotionBlur compositor work, but I still have the "choppiness problem", I don't know how to properly set the previous view-projection matrix in order to avoid this artifact. Hope someone will help to find a solution. I think the problem relies in my application non-constant frame-rate.

I'd like to contribute posting my compositor and CG shaders code. I had to flip some multiplications with matrices in order to match OGRE standard (the previous posts did not mention about it). I'm using Ogre 1.6.

Here's the compositor:

Code: Select all

// Motion Blur
// inspired by GPU Gems3. Chapter 27. Motion Blur as a Post-Processing Effect
// and used ideas from OGRE implementation found at:
// http://www.ogre3d.org/forums/viewtopic.php?f=2&t=35980#p432181

compositor EPFC_MotionBlur
{
    technique
    {
        // Depth: try to use a single channel floating point texture to save memory (instead of PF_FLOAT32_RGB or PF_FLOAT32_RGBA)
		// (we must use float32 instead of float16 in order to avoid precision artifacts)
		texture depth target_width target_height PF_FLOAT32_R
		// Scene
		texture scene target_width target_height PF_R8G8B8A8
       

        // the scene we want to apply MotionBlur to
        target scene
        {
            input previous
        }

        target depth
		// target_output
        {
            input none
            
			// use a material scheme to tell each object to render its depth
			material_scheme MS_MOTION_BLUR_DEPTH
			
            pass clear
            {
            }
			
            pass render_scene
            {		
            }
        }

		target_output
        {
            input none

            pass render_quad
            {
				material EPF_MotionBlur
				
                input 0 scene
                input 1 depth
               
				// ID this pass in order to find it
				// in compositor listeners
				identifier 999
            }
        } 
    } 
}

}
Here are the materials:

Code: Select all

fragment_program  EPF_MotionBlur_PS cg
{
	source MotionBlur.cg
	entry_point EPF_MotionBlur_PS
	profiles ps_3_0 arbfp1
}


material EPF_MotionBlur
{
	technique
    {
        pass
        {
			cull_hardware none
			cull_software none
			depth_func always_pass
			fog_override true

			vertex_program_ref EPF_StdQuad_VS
			{
			}

			fragment_program_ref EPF_MotionBlur_PS
			{
			}
			
			texture_unit scene
            {
				tex_coord_set 0
				tex_address_mode clamp
                filtering bilinear
            }

            texture_unit depth
            {
                tex_coord_set 0
				tex_address_mode clamp
				filtering bilinear
            }
            
        }
    }

}

//
//	Geometry Depth Shaders
//

vertex_program EPF_MotionBlur_Depth_VS cg
{
    source MotionBlur.cg
    profiles vs_1_1 arbvp1
    entry_point EPF_MotionBlur_Depth_VS

	default_params
	{
		param_named_auto wmat worldviewproj_matrix 
	}
}

fragment_program EPF_MotionBlur_Depth_PS cg
{
	source MotionBlur.cg
	entry_point EPF_MotionBlur_Depth_PS
	// cannot use ps_1_1 because compiler complains about zero-division
	profiles ps_2_0 arbfp1
}

And here are the CG shaders:

Code: Select all

#define NUM_SAMPLES 8

float4 EPF_MotionBlur_PS(
	float2 texCoord: TEXCOORD0,
	
	sampler2D SceneSampler : TEXUNIT0,
	sampler2D DepthSampler : TEXUNIT1,
    
	uniform float4x4 EPF_ViewProjectionInverseMatrix,
	uniform float4x4 EPF_PreviousViewProjectionMatrix
) : COLOR
{
	//	First, compute current pixel world-space position
	//
	// Get the depth buffer value at this pixel
	float zOverW = tex2D(DepthSampler, texCoord).r;  
	// H is the viewport position at this pixel in the range -1 to 1
	float4 H = float4(texCoord.x * 2 - 1, (1 - texCoord.y) * 2 - 1,  zOverW, 1);  
	// Transform by the view-projection inverse
	// float4 D = mul(H, EPF_ViewProjectionInverseMatrix);  
	// Use matrix - column vector multiplication (because OGRE works this way and provides xforms this way!)
	float4 D = mul(EPF_ViewProjectionInverseMatrix, H);  
	// Divide by w to get the world position
	float4 worldPos = D / D.w;  
	
	//	Then compute the per-pixel velocity vectors that determine the direction to blur the image
	//
	// Current viewport position  
	float4 currentPos = H;  
	// Use the world position, and transform by the previous view-projection matrix
	// float4 previousPos = mul(worldPos, EPF_PreviousViewProjectionMatrix);  
	// Use matrix - column vector multiplication (because OGRE works this way and provides xforms this way!)
	float4 previousPos = mul(EPF_PreviousViewProjectionMatrix, worldPos);  
	// Convert to nonhomogeneous points [-1,1] by dividing by w.  
	previousPos /= previousPos.w;  
	// Use this frame's position and last frame's to compute the pixel velocity  
	float2 velocity = (currentPos - previousPos)/2.f;  
	
	//	Then use the velocity vector at the current pixel to sample the color buffer multiple times to achieve the motion blur effect
	//
	// Get the initial color at this pixel 
	float4 base = tex2D(SceneSampler, texCoord);  
	float4 color = base;
	texCoord += velocity;  
	for(int i = 1; i < NUM_SAMPLES; ++i, texCoord += velocity)  
	{  
		// Sample the color buffer along the velocity vector
		float4 currentColor = tex2D(SceneSampler, texCoord);  
		// Add the current color to our color sum
		color += currentColor;  
	}  
	// Average all of the samples to get the final blur color.  
	float4 finalColor = color / NUM_SAMPLES;  
	
	return finalColor;
}

//
//	Geometry Depth Shaders
//
void EPF_MotionBlur_Depth_VS
(
	uniform float4x4 wmat, // world view projection xf
	
	in float4  pos : POSITION,
	
	out float4  oPos : POSITION,
	out float4 oDepth: TEXCOORD0
)
{
	oPos = mul(wmat,pos);   
	oDepth = oPos;
}

float4 EPF_MotionBlur_Depth_PS
(
	in float4 depth : TEXCOORD0
): COLOR0
{   
   float d = depth.z/depth.w;
   return float4(d, d, d, 1);
}
Could anyone help with "choppiness problem"?
Last edited by lordsme on Mon Jan 02, 2012 12:43 pm, edited 1 time in total.
My Portfolio
http://davidedigiannantonio.weebly.com

MESH - Mise-en-scene Helper
http://www.mesh-project.org/

ASALab @ Virtual Reality & Multimedia Park
http://www.vrmmp.it
User avatar
razi
Greenskin
Posts: 130
Joined: Tue Oct 14, 2008 5:54 pm
Location: Slovakia
x 17

Re: Motion Blur and previous viewProjection Matrix

Post by razi »

Its been some time since I finished version which I declared as final and integrated to my pipeline, but Ill help with what I can. I remember, after achieving real motion blur effect I had long time playing and analyzing problems with it. One time i seriously had to take break cause my eyes hurt from all the motion blurs :D .

For the obvious question how to smooth projections, I use this code in my application to get them, pVP and iVP are then sent to shader:

Code: Select all

	float interpolationFactor = mPreviousFPS*0.03f; //blur size constant

	Ogre::Quaternion estimatedOrientation = Ogre::Quaternion::Slerp(interpolationFactor,camnode->_getDerivedOrientation(),prevOr);
	Ogre::Vector3    estimatedPosition    = (1-interpolationFactor)*camnode->_getDerivedPosition()+interpolationFactor*prevPos;
	Ogre::Matrix4 viewMatrix =Ogre::Math::makeViewMatrix(estimatedPosition,estimatedOrientation);
	Ogre::Matrix4 projectionMatrix   = mCamera->getProjectionMatrix();
	*pVP = projectionMatrix*viewMatrix;

	*iVP=(projectionMatrix*mCamera->getViewMatrix()).inverse();

//for next frame
	mPreviousFPS=1/timeSinceLastFrame;
	prevPos=camnode->_getDerivedPosition();
	prevOr=camnode->_getDerivedOrientation();
One problem which was obvious for me was, that although blurring seemed working, when the camera was spinning very fast (doing 360 in fps camera) and suddenly stopped the screen jumped (there was obvious change of objects from being moved by blurring to zero blurring). Im not sure if it helped much, but I changed coordinates translation from simply moving to one side to move it to both sides (so I take half samples with +velocity and half samples with -velocity).
Another problem, which I actually felt the whole time but never could actually explain was, that I eventually found out that x coordinate of velocity was opposite to what I wanted (*-1). (I found it out once again by doing 360 in fps camera while looking at my feet, the ground was not blurred in circle with center below me as expected, but rather in circles around me, you could say star shape blur under my feet) Was it bad projections or something else, I dont know. This might have helped the first problem too, dont remember.
User avatar
lordsme
Gremlin
Posts: 167
Joined: Sun Mar 11, 2007 1:11 pm
Location: Turin, Italy
x 10
Contact:

Re: Motion Blur and previous viewProjection Matrix

Post by lordsme »

Thank you very much razi,
I still have some choppiness problem but it's my fault, I have some problem with my view matrix that changes very quickly because of my navigation engine.

By the way, here I post the code from my framework that updates the shader uniform parameters from my previous post.
There are some calls from my framework API, but I think that it's readable anyway.
Everything is very close to razi's solution, I just added a time multiplier to gain more control on MotionBlur.

Code: Select all

void updateMotionBlur()
{

// get Ogre camera
Ogre::Camera* mCamera = m_pViewer->getActiveCamera()->_getOgreCamera();
// get current time (seconds)
float current_time = m_pViewer->getSimulationTime();

// compute view projection inverse matrix
Ogre::Matrix4 projectionMatrix   = mCamera->getProjectionMatrix();
Ogre::Matrix4 iVP = (projectionMatrix * mCamera->getViewMatrix()).inverse();

float interpolationFactor = m_lastFPS * 0.03f * m_timeScale; // m_timeScale is a multiplier to control motion blur interactively
Ogre::Quaternion current_orientation = mCamera->getDerivedOrientation();
Ogre::Vector3 current_position = mCamera->getDerivedPosition();
Ogre::Quaternion estimatedOrientation = Ogre::Quaternion::Slerp(interpolationFactor, current_orientation, (*m_pPreviousOrientation));
Ogre::Vector3 estimatedPosition    = (1-interpolationFactor) * current_position + interpolationFactor * (*m_pPreviousPosition);
Ogre::Matrix4 prev_viewMatrix = Ogre::Math::makeViewMatrix(estimatedPosition, estimatedOrientation);
// compute final matrix
*m_pPreviousViewProjectionMatrix = projectionMatrix * prev_viewMatrix;

// update position and orientation for next update time
*m_pPreviousOrientation = current_orientation;
*m_pPreviousPosition = current_position;
m_lastFPS = 1.0f /(current_time - m_lastTime);
m_lastTime = current_time;

// update shader
Ogre::GpuProgramParametersSharedPtr fp_params = m_pListener->getBlendMaterial()->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
// update view projection inverse matrix
fp_params->setNamedConstant("EPF_ViewProjectionInverseMatrix", iVP);
// update previous view projection matrix
fp_params->setNamedConstant("EPF_PreviousViewProjectionMatrix", *m_pPreviousViewProjectionMatrix);
}
My Portfolio
http://davidedigiannantonio.weebly.com

MESH - Mise-en-scene Helper
http://www.mesh-project.org/

ASALab @ Virtual Reality & Multimedia Park
http://www.vrmmp.it
User avatar
lordsme
Gremlin
Posts: 167
Joined: Sun Mar 11, 2007 1:11 pm
Location: Turin, Italy
x 10
Contact:

Re: Motion Blur and previous viewProjection Matrix

Post by lordsme »

I posted a demo of this MotionBlur implementation here:
http://www.ogre3d.org/forums/viewtopic.php?f=11&t=68303

Enjoy
My Portfolio
http://davidedigiannantonio.weebly.com

MESH - Mise-en-scene Helper
http://www.mesh-project.org/

ASALab @ Virtual Reality & Multimedia Park
http://www.vrmmp.it
User avatar
TheGameMaker
Kobold
Posts: 26
Joined: Fri Jun 18, 2010 4:19 pm

Re: Motion Blur and previous viewProjection Matrix

Post by TheGameMaker »

Hello. I seem to be bringing up a slightly old thread but I wanted to test this with the latest OGRE (1.8.1 right now) :D

I have implemented the above code but I am having a problem with one aspect of this motion blur - getting the ViewProjectionInverseMatrix and the PreviousViewProjectionMatrix. I used the code above but changed one line:

This

Code: Select all

m_lastFPS = 1.0f /(current_time - m_lastTime);
to this

Code: Select all

m_lastFPS = mWindow->getStatistics().lastFPS;
That just seemed to work better for me. My code now looks like this:

Code: Select all

// compute view projection inverse matrix
	Ogre::Matrix4 projectionMatrix   = mCamera->getProjectionMatrix();
	Ogre::Matrix4 iVP = (projectionMatrix * mCamera->getViewMatrix()).inverse();

	float interpolationFactor = m_lastFPS * 0.03f * motionBlurTimeScale; // m_timeScale is a multiplier to control motion blur interactively
	Ogre::Quaternion current_orientation = mCamera->getDerivedOrientation();
	Ogre::Vector3 current_position = mCamera->getDerivedPosition();
	Ogre::Quaternion estimatedOrientation = Ogre::Quaternion::Slerp(interpolationFactor, current_orientation, m_pPreviousOrientation);
	Ogre::Vector3 estimatedPosition    = (1-interpolationFactor) * current_position + interpolationFactor * m_pPreviousPosition;
	Ogre::Matrix4 prev_viewMatrix = Ogre::Math::makeViewMatrix(estimatedPosition, estimatedOrientation);
	// compute final matrix
	m_pPreviousViewProjectionMatrix = projectionMatrix * prev_viewMatrix;

	// update position and orientation for next update time
	m_pPreviousOrientation = current_orientation;
	m_pPreviousPosition = current_position;
	m_lastFPS = mWindow->getStatistics().lastFPS;
	m_lastTime = current_time;

	// update shader
	Ogre::GpuProgramParametersSharedPtr fp_params = mBlurListener->getBlendMaterial()->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
	// update view projection inverse matrix
	fp_params->setNamedConstant("hardcoded_ViewProjectionInverseMatrix", iVP);
	// update previous view projection matrix
	fp_params->setNamedConstant("hardcoded_PreviousViewProjectionMatrix", m_pPreviousViewProjectionMatrix);
Now the motion blur seems to work but as I move the camera further away from the center of the scene, it starts to shake and get unstable.

So does anyone know why this happens? Can it be fixed or is it some kind of limitation of floats or something?
Post Reply