I have a 2D fixed-timestep simulation (a bunch of moving sprites) that ticks several times per render frame.

I would like to render the state of each tick, so that all the ticks between render frames A and A+1 contribute to the image at A+1: a discrete approximation of motion blur. I assumed this would be trivial, but so far I'm pretty stumped!

My current approach is: given N sim ticks, draw the sprites at each tick with alpha = 1/N. However, so far this feels like it's not the correct approach, and I was hoping someone here could point me in the right direction.

I've tried alpha compositing (ie equivalent to (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)), but this doesn't work: the sprites all become somewhat transparent, because by definition if you alpha-composite N images which have alpha < 1, you'll always get some background colour blended into the result.

Then I tried additive blending (ie (GL_ONE, GL_ONE)): this produces the correct result in isolation (ie a stationary sprite is drawn fully opaque, since it gets drawn N times with 1/N alpha; a moving sprite is opaque where all its ticks overlap, and transparent at the edges where only some ticks overlap), however if there's anything behind the sprites (which there will be – ie sprites moving through each other, a parallax background, etc.) then the additive blend ruins this by making everything over-bright.

Am I missing something basic?? This seems like it should be pretty straightforward, but so far all I can think of is that I would need to composite things in multiple steps: for each sprite, use additive blending to render it into a buffer, then alpha-composite that buffer with the rest of the scene.

Is that really the best I can do? It seems ugly and complicated; why shouldn't I be able to draw the background image, along with everything else in the scene, N times with 1/N alpha?

Everything I've found via google is for pixel shaders which approximate motion blur per-object via multisampling. In my case, I already have the scene multi-sampled, I just don't understand how to correctly blend all the samples together!

Anyway, any tips or pointers are greatly appreciated! Thanks. : )


You can draw your first tick at 100% alpha.

Your second tick can be drawn at 50% alpha, so you get 50% of tick 1 showing through.

Your third tick can be drawn at 33% alpha, so you get 67% of ticks 1+2 (50% each, so about 33% net).

Your fourth tick can be drawn at 25% alpha, so you get 75% of ticks 1-3 (at 33% each, so 25% net).

And so on. The \$i^{\text{th}}\$ layer can be drawn with alpha \$\frac 1 i\$ to get approximately equal weighting of this and all underlying layers.

Note however that if you want an object to be opaque relative to things it overlaps in a single tick, then you'll want to pre-compose the entire rendered frame for the tick into its own buffer using the sprites' normal alpha values. Then you can blend that pre-composed frame with the accumulated frames from previous ticks in a separate buffer.

  • \$\begingroup\$ Thanks -- the full-screen compositing idea feels like the way to go, I'm not sure why I didn't think of it. (Maybe: because of the horrible amount of fillrate that's going to eat! ;-; ) AFAICT "start at 100% alpha" doesn't work: imagine an object that moves by more than its radius each tick, it should be drawn as N sprites with equal transparency -- it doesn't make sense that the earliest tick is fully opaque. (Also I was hoping to get "free" anti-aliasing by jittering the position of each sprite by a sub-pixel amount each tick, which means proper compositing/uniform alpha per tick.) \$\endgroup\$ Aug 12 at 14:08
  • \$\begingroup\$ You'd mentioned that your game includes a background, so the background in tick 2 should draw over the sprite from tick 1, dimming it down to 50% opacity even if the object moved enough to not overlap itself. If that's not the case, you should edit your question to clarify. \$\endgroup\$
    – DMGregory
    Aug 12 at 18:35
  • \$\begingroup\$ ah right! Sorry I wan't thinking about this properly; I'll give it a shot.. \$\endgroup\$ Aug 13 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.