пятница, 18 мая 2012 г.

Motion Blur



Hello. today I want to show my implementation of motion blur effect. First of all, lets see simple clip without any effect. Here it is:

                 

As you might guess ball represented with 2 triangles ant this texture (note that there're free space around colored area):



So, what wrong with such simple animation? Yeah, you right - it's boring. Let add some dynamics:

                 

Here I've added motion blur by sample texture in direction of velocity. Velocity is computed as difference in positions in current and previous frames. Also texture sampling done in positive and negative direction of velocity. Step of texture lookup offset is computed in vertex shader and passes to fragment shader as varing. There're limitations on this step size - it's done to avoid jaggies in case of too big offset.

To improve visual appearance I've added geometry stretching in direction of velocity. To be honest, my current implementation not good. But for this particular image it looks more or less pleasurable. Stretching is implemented simply by adding some scale of velocity to vertex. Also I take into account position of vertices, i.e. forward or backward. Here is the demo (you can see stretching better if you decrease frame rate with numeric stepper):

                 

The last demo shows combination of motion blur and geometry stretching:

                 

The shaders I wrote using AGALMacroAssembler. It's much better than raw AGAL but it's buggy. Hey, Adobe, we definitely need new high level shader language!

Vertex shader:

//attributes
alias va0, pos;
alias va1, uv;

//constants
alias vc0, projectionMatrix;
alias vc4, prevTransformMatrix;
alias vc8, currTransformMatrix;
alias vc12, CONST_12(3000, 0.0001, 0.01, 1);
alias vc13, CONST_13(5000, 0, 0, 0);

//temps
alias vt0, prevPos;
alias vt1, currPos;
alias vt2, velocity;
alias vt3, temp;
alias vt4, oneOrZero;
alias vt5, offsetSign;

//varyings
alias v0, velocityOut;
alias v1, uvOut;

//shader
prevPos = mul4x4(pos, prevTransformMatrix);
currPos = mul4x4(pos, currTransformMatrix);

velocity = currPos - prevPos;
velocity /= 3000; // scale down to get values appropriate for fragment shader without further divisions

//all this stuff is only for velocity magnitude check. If it greater than some treshold, that set it to that treshols
dp3 temp velocity velocity;
slt oneOrZero temp 0.0001;
nrm temp.xyz velocity;
velocity *= oneOrZero;
temp *= 0.01;
oneOrZero = 1 - oneOrZero;
temp *= oneOrZero;
velocity += temp;

//here I move vertex along velocity according to direction, i.e. 'first' vertices I move forward and 'last' bakward
dp3 temp pos velocity;
offsetSign = temp;
abs temp temp;
offsetSign /= temp;
temp = velocity;
temp *= offsetSign;
temp *= 5000;
currPos.xy += temp.xy;

op =  mul4x4(currPos, projectionMatrix);

velocityOut = velocity;
uvOut = uv;
Fragment shader is ugly because of repeating of the same part several times. And I don't know how to make macroses work - I get an error if I use macros with texture sample opcode:

//constants
alias fc0, CONST_0(21, 0, 0, 0);

//textures
alias fs0, texture;

//temps
alias ft1, color;
alias ft2, stepLen;
alias ft3, uvOffsetPlus;
alias ft4, uvOffsetMinus;
alias ft5, colorOffset;

//varyings
alias v0, velocityIn;
alias v1, uvIn;

//shader
tex color, uvIn, texture<2d, linear, nomip>;

stepLen = velocityIn;

uvOffsetPlus = uvIn;
uvOffsetMinus = uvIn;

//1
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//2
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
//color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//3
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//4
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//5
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//6
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//7
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//8
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//9
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

//10
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;

oc = color / 21;

3 комментария:

  1. Hi, Atula. Unfortunately, I can't share source. I'm using a lot of my math classes instead of adobes classes and posting my code will confuse a lot. But I post most challenging part - shaders - where (in declaration) you can see all necessary data need to be passed.

    ОтветитьУдалить