Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

motion blur frustration


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
6 replies to this topic

#1 Yours3!f   Members   -  Reputation: 544

Like
0Likes
Like

Posted 24 July 2012 - 05:29 AM

Hi,

I'm implementing motion blur, in particular this one: http://mynameismjp.w...on-blur-sample/
All went well, however I noticed a tiny problem: MJP doesn't encode the velocity information. I thought no problem, let's do a scale-bias, so that the coordinates get transformed from [-1, 1] to [0, 1], and then I can store them in an RGBA texture's xy channels.
Then came the surprise, when I do motion blur, the whole scene became blurred a bit, and when I moved around I saw screen tearing everywhere. The first symptom's cause is obvious, for some reason the precision isn't that good, and therefore 0 * 0.5 + 0.5 = x became x > 0.5.
Where x is the encoded velocity. Now where x should be 0 (no movement) it was around 0.005.
So I added a threshold, if x < 0.006 then no blur. Hooray this solved the blurring problem, but not the tearing.
So I started to investigate this further, as the technique seemed to be very easy to implement I suppose I did it correctly. Now When I masked the screen ( white where blur, black where no blur) I noticed that as I move around certain areas fall below the threshold and therefore they don't get blurred. This symptom is most noticable when I move horizontally. After a certain point the screen is no longer blurred. See on video:



and here's the source code:

c++ g-buffer rendering code:
[c++ code, rendering]
  o->mvm.push_matrix();

  mm::vec4 diff_color( 0.9f, 0.9f, 0.9f, 1.0f );
  coloring_shader->bind();

  coloring_shader->pass_m4x4( o->ppl.get_model_view_matrix(), "modelview" );
  coloring_shader->pass_m4x4( o->ppl.get_projection_matrix(), "proj" );
  coloring_shader->pass_m3x3( o->ppl.get_normal_matrix( false ), "normal_mat" );
  coloring_shader->pass_vec4( diff_color, "diff_color" );
  coloring_shader->pass_float( -1.0f / event::get()->get_resize()->get_data()->far, "inv_neg_far" );
//in the first frame I upload a identity matrix as the previous, in the next frame it is fixed.
  coloring_shader->pass_m4x4( o->ppl.get_projection_matrix() * coloring_prev, "previous_mat" );
  coloring_prev = o->mvm.get_matrix();

  render_mesh( test_mesh );

  coloring_shader->unbind();

  o->mvm.pop_matrix();

G-buffer rendering (including velocity buffer)
[vertex shader]
#version 410

uniform mat4 modelview, proj;
uniform mat3 normal_mat;
uniform mat4 previous_mat;

in vec3 in_normal;
in vec4 in_vertex;

out cross_shader_data
{
  vec3 vs_normal;
  vec4 vs_position;
  vec4 prev_vector;
  vec4 curr_vector;
} o;

void main()
{
  o.vs_normal = normalize(normal_mat * in_normal);
  o.vs_position = modelview * in_vertex;

  o.prev_vector = previous_mat * in_vertex;
  o.curr_vector = proj * o.vs_position;

  gl_Position = o.curr_vector;
}

[pixel shader]
#version 410

uniform vec4 diff_color;
uniform float inv_neg_far;

in cross_shader_data
{
  vec3 vs_normal;
  vec4 vs_position;
  vec4 prev_vector;
  vec4 curr_vector;
} i;

out vec4 albedo;
out vec4 normal;
out vec4 depth;
out vec4 extra;

void main()
{
  albedo = diff_color;
  normal = vec4(i.vs_normal * 0.5 + 0.5, 1.0);
  depth.x = i.vs_position.z * inv_neg_far;
//transform clip space to ndc, then scale / bias to store it
  extra = vec4((i.curr_vector.xy / i.curr_vector.w - i.prev_vector.xy / i.prev_vector.w) * 0.5 + 0.5, 0, 0);
}

blurring pass:
#version 410

uniform sampler2D texture0; //shading result
uniform sampler2D texture1; //velocity buffer

in cross_shader_data
{
  vec2 tex_coord;
} i;

out vec4 color;

void main()
{
  vec2 velocity_vec = texture(texture1, i.tex_coord).xy * 2.0 - 1.0; //scale and bias back

  if(length(velocity_vec) < 0.006)
  {
	color = texture(texture0, i.tex_coord);
	return;
  }

  vec4 result = vec4(0.0);
  int num_of_samples = 16;

  //clamp the velocity to make sure that there's no undersampling
  vec2 max_velocity = (2.0 * float(num_of_samples)) / textureSize(texture0, 0);
  velocity_vec = clamp(velocity_vec, -max_velocity, max_velocity);

for(int c = 0; c < num_of_samples; c++)
{
  vec2 sample_coord = i.tex_coord + (float(velocity_vec) * (c / float(num_of_samples)));
  //sample & add to result:
  result += texture(texture0, sample_coord);
}
color = result / float(num_of_samples);
}

any ideas what am I doing wrong / how to solve this?

Edited by Yours3!f, 24 July 2012 - 05:45 AM.


Ad:

#2 Yours3!f   Members   -  Reputation: 544

Like
0Likes
Like

Posted 28 July 2012 - 12:00 PM

ok, I eliminated most of the unneded blurring properly. Firstly I had to make the velocity buffer to use nearest sampling then I had to modify the g-buffer shader, because it outputted the velocity vectors in wrong range:
extra = vec4((i.curr_vector.xy / i.curr_vector.w - i.prev_vector.xy / i.prev_vector.w) * 0.5 + 0.5, 0, 0);
-->
extra = vec4((i.curr_vector.xy / i.curr_vector.w - i.prev_vector.xy / i.prev_vector.w) * 0.25 + 0.5, 0, 0);

then I had to change the checking in the motion blur shader:
vec2 velocity_vec = texture(texture1, i.tex_coord).xy;

//128 (0.5 in shader) / 255 is something like 0.501... this is why we do it like this
  if(velocity_vec * 255 == vec2(128))
  {
	color = texture(texture0, i.tex_coord);
	return;
  }

  velocity_vec = velocity_vec * 2.0 - 1.0; //scale and bias back

the tearing problem is still present although it is less noticable...

Edited by Yours3!f, 28 July 2012 - 12:00 PM.


#3 MJP   Moderators   -  Reputation: 5418

Like
0Likes
Like

Posted 28 July 2012 - 12:36 PM

In my experience, 10 bits has generally not been enough precision for encoding velocity. The quantization can result in noticable banding, and of course you have the "0.5" problem. Currently I like to store velocity in a 1010102 format, where X and Y are velocity, Z is depth (since doing a decent motion blur requires comparing both depth and velocity of each sample), and W is the sign bits. Having the sign bits separate lets you avoid the 0.5 problem.

#4 Yours3!f   Members   -  Reputation: 544

Like
0Likes
Like

Posted 29 July 2012 - 01:08 AM

In my experience, 10 bits has generally not been enough precision for encoding velocity. The quantization can result in noticable banding, and of course you have the "0.5" problem. Currently I like to store velocity in a 1010102 format, where X and Y are velocity, Z is depth (since doing a decent motion blur requires comparing both depth and velocity of each sample), and W is the sign bits. Having the sign bits separate lets you avoid the 0.5 problem.


Thank you MJP for the reply,

I'll try this out.

#5 Yours3!f   Members   -  Reputation: 544

Like
0Likes
Like

Posted 03 August 2012 - 09:58 AM

hi,

I have a few quiestions concerning the packing. I currently put the velocity vectors simply as before in the xy channels:
vec2 ndc_vel = i.curr_vector.xy / i.curr_vector.w - i.prev_vector.xy / i.prev_vector.w; //in range -2 ... 2 because of subtraction
extra = vec4(ndc_vel * 0.25 + 0.5, 0, 0); //this is now RGB10_A2 (xy in range 0...1)
the quality has improved significantly, now I can't see any tearing!
How do I extract the sign bit of each vector? I looked up bit masking / flagging but I can't think of how to use that.
If I could store 2 bits unsigned, the I could flag each bit like 2 would mean the x vector is negative and 1 would mean that the y is. How do I do this in glsl?
unsigned char sign_bits = 0;
sign_bits |= 2;
sign_bits |= 1;
also should I store the velocity vectors abs()-ed? (because the sign is stored in A)
encoding the depth isn't a problem thanks to your tutorials :)

#6 MJP   Moderators   -  Reputation: 5418

Like
0Likes
Like

Posted 04 August 2012 - 08:03 PM

How do I extract the sign bit of each vector? I looked up bit masking / flagging but I can't think of how to use that.
If I could store 2 bits unsigned, the I could flag each bit like 2 would mean the x vector is negative and 1 would mean that the y is. How do I do this in glsl?


Yeah you have right idea. You'll want bit 0 to the the sign bit for X, and bit 1 to be the sign bit for Y. If you have support for integer operations (I'm not sure which GLSL version is required for integer ops), you can do it just the way you described by OR'ing the shifted bit if the corresponding value is negative. So you'd do

uint sign_bits = 0;
if(velocity.x < 0.0f)
    sign_bits |= (1 << 0); // set bit 0
if(velocity.y < 0.0f)
    sign_bits |= (1 << 1); // set bit 1

If you don't have integer ops you can do it with floating point:

float sign_bits = 0.0f;
f(velocity.x < 0.0f)
    sign_bits += 1.0f;
if(velocity.y < 0.0f)
    sign_bits += 3.0f;

Then you'll need to convert the sign bits to a [0, 1] value, since that's what the texture format expects. To do that you can just divide by 3.0f.

also should I store the velocity vectors abs()-ed? (because the sign is stored in A)


Yes, you'll want to store the absolute value of your velocity. Then when you sample the velocity texture later on, you'll need to extract the sign bits from the alpha value and negate the corresponding velocity component based on the value of those bits.

uint sign_bits = uint(velocity_alpha * 3.0f);
uint x_sign = sign_bits & 0x1; // mask out everything but bit 0
uint y_sign = (sign bits & 0x3) >> 1; // mask out everything but bit 1, and shift it to the right 1 bit
if(x_sign)
	 velocity.x *= -1.0f;
if(y_sign)
	 velocity.y *= -1.0f;


#7 Yours3!f   Members   -  Reputation: 544

Like
0Likes
Like

Posted 05 August 2012 - 09:30 AM


How do I extract the sign bit of each vector? I looked up bit masking / flagging but I can't think of how to use that.
If I could store 2 bits unsigned, the I could flag each bit like 2 would mean the x vector is negative and 1 would mean that the y is. How do I do this in glsl?


Yeah you have right idea. You'll want bit 0 to the the sign bit for X, and bit 1 to be the sign bit for Y. If you have support for integer operations (I'm not sure which GLSL version is required for integer ops), you can do it just the way you described by OR'ing the shifted bit if the corresponding value is negative. So you'd do

uint sign_bits = 0;
if(velocity.x < 0.0f)
	sign_bits |= (1 << 0); // set bit 0
if(velocity.y < 0.0f)
	sign_bits |= (1 << 1); // set bit 1

If you don't have integer ops you can do it with floating point:

float sign_bits = 0.0f;
f(velocity.x < 0.0f)
	sign_bits += 1.0f;
if(velocity.y < 0.0f)
	sign_bits += 3.0f;

Then you'll need to convert the sign bits to a [0, 1] value, since that's what the texture format expects. To do that you can just divide by 3.0f.

also should I store the velocity vectors abs()-ed? (because the sign is stored in A)


Yes, you'll want to store the absolute value of your velocity. Then when you sample the velocity texture later on, you'll need to extract the sign bits from the alpha value and negate the corresponding velocity component based on the value of those bits.

uint sign_bits = uint(velocity_alpha * 3.0f);
uint x_sign = sign_bits & 0x1; // mask out everything but bit 0
uint y_sign = (sign bits & 0x3) >> 1; // mask out everything but bit 1, and shift it to the right 1 bit
if(x_sign)
	 velocity.x *= -1.0f;
if(y_sign)
	 velocity.y *= -1.0f;


hi,

I tried it and works wonderfully! Thank you!!!




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS