Motion vectors in shaders and the Godot engine

Godot Version

4.6.1.stable

Question

How does the motion vector buffer actually work..?

What range of values can be stored in them, and what is their meaning? I can’t find a single crumb of information on the topic, since compositor effects are completely undocumented.

I have the following pseudocode in my compositor effect:

void main() {
	...
	
    vec4 motion = imageLoad(motion_tex, uvr);
    motion.rg = motion.rg * amount;
	
	vec4 color = imageLoad(prev_tex, ivec2(floor(vec2(uv) + vec2(motion.rg))));
	
	...
}

Where motion_tex receives the motion vector texture, and previous_tex receives the frame buffer of the previous frame.

Is this implementation correct or not..? I tried many iterations of manipulating the motion texture, but all of them looked wrong, and this one is the least wrong one, although it still looks wrong, just a little bit.

I really want to sort this out before making a tutorial and spreading misinformation…

Have you tried just displaying the motion texture?

Yes!

It was the first thing I did. I’m not entirely sure if it’s -1.0 to 1.0 or 0.0 to 1.0, because values under 0.0 are all rendered equally to black. Manipulating the texture by halving and adding 0.5 displays nonhelpful grey that doesn’t look too distinguishable in my monitor.

Besides, I want to double check if my observations are correct.

You can test if it’s signed by moving in spatial diagonal whose all 3 components are negative. If it renders fully black - it’s signed.

That helped, it is signed.

Now, are they truly normalized o are they in random units?

I don’t know exactly for sure, there isn’t any official documentation on the matter, and I can’t just read up on it.

It’s a compute shader. You can actually write the texture verbatim into a floating point buffer, copy the buffer to the cpu side and print it out.

1 Like

I tested a little more and the following seems to be correct. It seems like it’s a normalized, signed, vector, from my testing. Also, I forgor to define uv.

void main() {
	...
	
	ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
	
	...
	
    vec4 motion = imageLoad(motion_tex, uvr);
    motion.rg = motion.rg * amount;
	
	vec4 color = imageLoad(prev_tex, ivec2(floor(vec2(uv) + vec2(motion.rg * uv))));
	
	...
}

This looks correct, yeah, and I’ll probably just write the tutorial with this information in mind, but it’d sure be nice to have an actual documentation ;A;

1 Like

You’ve seen this I assume ?

That’s depth. It’s a completely different textue, encoded in a similar, but different way. The only similarity is that the vector axis’ max value is unitary, even the range is completely different.

I read this years ago when I started to study shaders, and the page hasn’t changed, despite it being outdated by now.

It’s not really outdated. The techniques it describes are perfectly valid, it just doesn’t deal with the Compositor which is kinda next level advanced.

Right. Brain flatulence, it was right in my face, motion but I saw depth. Oops. Sorry.

Eh, it’s not too much more advanced, it’s just more abstract. either way, I’ll mark my comment as the solution.

You can contribute the tutorial to the documentation. It’s open source.

3 Likes

I don’t know how to git…

It’s super simple, check these out:

It describes the pull request workflow too. It may be overwhelming in theory, but it’s much easier in practice. Just try it once and you’ll find for yourself.

3 Likes