Author Topic: Un article sur le flou de mouvement  (Read 6867 times)

0 Members and 1 Guest are viewing this topic.

Offline Zavie

Un article sur le flou de mouvement
« on: 11 January 2011 à 03:18:53 »
Et un titre en français pour compenser le fait que l'article est en anglais !

Voici une explication détaillée de la technique de flou de mouvement que l'on utilise actuellement.

Les commentaires sont bien sûr les bienvenus, d'autant que c'est mon premier article technique. Avec patapom qui semble pris d'une frénésie prolifique, il fallait bien réagir. ;-)

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Un article sur le flou de mouvement
« Reply #1 on: 11 January 2011 à 15:05:35 »
Intéressant !

Juste pour spéculer : ça ferait quoi à ton avis si on utilisait un algo de ce genre :

Code: [Select]
Buffers[0] = RenderScene();  // On rend la scène (standard)
Buffers[1] = Buffers[1] * 0.8 + Buffers[0]; // Le facteur 0.8 implique une trainée longue
Buffers[2] = Buffers[2] * 0.4 + Buffers[0]; // Le facteur 0.4 implique une trainée un peu moins longue
Buffers[3] = Buffers[3] * 0.2 + Buffers[0]; // Et 0.2 implique une trainée courte

FinalBuffer  = MotionBlur( Buffers );

Et :
Code: [Select]
float4  MotionBlur( float4[] _Buffers, float _Velocity ) : COLOR   // Ici, _Velocity est la norme du vecteur vitesse, calculée comme dans ta doc...
{
   if ( _Velocity < 1.0 )
     return lerp( _Buffers[0], _Buffers[1], _Velocity );   // Vitesse [0,1[
   else if ( _Velocity < 2.0 )
     return lerp( _Buffers[1], _Buffers[2], _Velocity-1.0 );   // Vitesse [1,2[
   else if ( _Velocity < 3.0 )
     return lerp( _Buffers[2], _Buffers[3], _Velocity-2.0 );   // Vitesse [2,3[
   else
     return _Buffers[3];   // Au delà de 3...
}

Les vitesses sont bien entendu à calculer et normaliser correctement.
C'est juste une idée comme ça, je sais pas si ça peut marcher vu que j'ai jamais fait de motion blur. Mais du coup avec ce genre d'algo, ça fait pas beaucoup d'opérations et ça devrait prendre en compte les artefacts de la boule rouge dans ton application, puisqu'elle sera ajoutée dans le calcul.

Mais j'dis ça, p'têt que mes lerps vont rendre un truc tout pourri. Quelqu'un a déjà essayé ?
« Last Edit: 11 January 2011 à 15:07:43 by Patapom »
.  Pom  .

Offline xoofx

  • Base
    • Pouet.net
    • View Profile
    • xoofx
  • Ancienneté: 1989
  • Groupe: FRequency
  • Rôle: code (+musique), web
  • Ville: Grenoble
Re : Un article sur le flou de mouvement
« Reply #2 on: 11 January 2011 à 15:10:25 »
Mais j'ids ça, p'têt que mes lerps vont rendre un truc tout pourri. Quelqu'un a déjà essayé ?
Ca serait pas similaire à la technique classique de motion blurr par alphablending?
Le problème avec cette technique, c'est que si tu rames pour faire le rendu d'une image, tu n'auras plus un motion-blur mais une trainé saccadée d'images bien nettes à l'écran!

[Edit]Sinon, Merci Zavie, je vais lire ça à l'occasion!  ;)[/Edit]

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Un article sur le flou de mouvement
« Reply #3 on: 11 January 2011 à 15:27:23 »
Ca serait pas similaire à la technique classique de motion blurr par alphablending?
Si, c'est pareil justement, mais avec plusieurs buffers et différentes tailles de trainées.

Le problème avec cette technique, c'est que si tu rames pour faire le rendu d'une image, tu n'auras plus un motion-blur mais une trainé saccadée d'images bien nettes à l'écran!
Bah 1) suffit de pas ramer ;D et 2) l'interpolation avec le buffer approprié tient compte du delta time avec les frames précédentes, effectivement t'auras moins de frames si tu rames et ça risque de se voir. D'où ma question : quelqu'un a déjà essayé ? ;D
.  Pom  .

Offline xoofx

  • Base
    • Pouet.net
    • View Profile
    • xoofx
  • Ancienneté: 1989
  • Groupe: FRequency
  • Rôle: code (+musique), web
  • Ville: Grenoble
Re : Un article sur le flou de mouvement
« Reply #4 on: 11 January 2011 à 15:37:03 »
D'où ma question : quelqu'un a déjà essayé ? ;D
Seulement la version simple buffer en alpha qui laisse des grosses trames dès que ça rame ;D

Offline MsK`

  • Base
    • Pouet.net
    • View Profile
  • Rôle: Code
  • Ville: Paris/RP
Re : Un article sur le flou de mouvement
« Reply #5 on: 12 January 2011 à 01:05:23 »
Il me semble avoir déjà vu un truc dans le genre de la technique de Zavie, mais avec un twist en plus, un papier d'ATI il me semble. Le rendu du velocity buffer est fait à part et avec un vertex shader spécial qui déforme le mesh rendu en fonction de son mouvement : si la normale pointe dans la direction du mouvement, alors le vertex shader renvoie la position actuelle, si la normale pointe dans la direction opposée au mouvement, le vertex shader envoie la position précédente. (il faut donc envoyer les 2 matrices de transformation au VS) C'est un gros hack mais qui permet d'éviter l'absence d'info de vitesse dans les zones ou le mesh n'y est plus.
Sinon niveau motion blur super cheaté, y'a la technique valve : tu blur l'image uniquement en fonction du mouvement de la caméra. Le rendu est excellent, les performances sont la, mais bien sur, si un objet bouge à l'écran et toi non, pas de blur dessus ^^

Edit: http://developer.nvidia.com/docs/IO/8230/GDC2003_OpenGLShaderTricks.pdf
Ah bah non, nvidia.
« Last Edit: 12 January 2011 à 01:07:24 by MsK` »

Offline ponce

Re : Un article sur le flou de mouvement
« Reply #6 on: 12 January 2011 à 03:32:48 »
Quote
Sinon niveau motion blur super cheaté, y'a la technique valve : tu blur l'image uniquement en fonction du mouvement de la caméra. Le rendu est excellent, les performances sont la, mais bien sur, si un objet bouge à l'écran et toi non, pas de blur dessus ^^
J'avais voulu faire ça, c'est cool, pas besoin de velocity map, sauf que ma caméra suivait le joueur qui lui même bougeait vite du coup le joueur se retrouvait blurré => gros fail.

Offline ponce

Re : Un article sur le flou de mouvement
« Reply #7 on: 12 January 2011 à 03:51:07 »
Quote
Quelqu'un a déjà essayé ?
@patapom

L'inconvénient 1 que je vois avec ta technique c'est que le retard de phase est différent avec ton filtre selon la vélocité.
Donc tu as beau filtrer "comme dans une mipmap" selon le vecteur vitesse mathématiquement c'est un peu faux car ton filtre ne préserve pas la phase, et donc si ta caméra s'arrête le temps va s'accélerer pendant un moment :) alors que si tu accéleres ta camera (sur une scène statique hein) alors le temps va ralentir.

L'inconvénient 2 que je vois c'est que les méthodes a feedback c'est pas très cool aux bordures et ca sera dur d'avoir un résultat indépendant du FPS (cf ce thread) en plus si les couleurs sont pas dans un espace linéaire ça se complique.

Quote
Le problème avec cette technique, c'est que si tu rames pour faire le rendu d'une image, tu n'auras plus un motion-blur mais une trainé saccadée d'images bien nettes à l'écran!
@lx a raison car ce que tu voudrais approcher c'est une intégrale parfaite, Zavie fait 3 assomptions pour l'approcher et éviter d'avoir à sampler temporellement. Plus tu rajoute de buffers, plus ton retard de phase aux plus gros "niveaux" va augmenter.

Offline MsK`

  • Base
    • Pouet.net
    • View Profile
  • Rôle: Code
  • Ville: Paris/RP
Re : Un article sur le flou de mouvement
« Reply #8 on: 12 January 2011 à 09:39:09 »
J'avais voulu faire ça, c'est cool, pas besoin de velocity map, sauf que ma caméra suivait le joueur qui lui même bougeait vite du coup le joueur se retrouvait blurré => gros fail.
C'est pour ça que valve rend le flingue après avoir fait le motion blur.

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Re : Un article sur le flou de mouvement
« Reply #9 on: 12 January 2011 à 13:41:29 »
L'inconvénient 1 que je vois avec ta technique c'est que le retard de phase est différent avec ton filtre selon la vélocité.
Donc tu as beau filtrer "comme dans une mipmap" selon le vecteur vitesse mathématiquement c'est un peu faux car ton filtre ne préserve pas la phase, et donc si ta caméra s'arrête le temps va s'accélerer pendant un moment :) alors que si tu accéleres ta camera (sur une scène statique hein) alors le temps va ralentir.

Mouais, ça  me semble pas clair ton truc. Pourquoi ça accélèrerait si la caméra s'arrête ? Si la caméra s'arrête sur une scène fixe alors vitesse == 0 et donc je sample le buffer #0 qui est celui qui vient juste d'être rendu et qui n'est pas blurré...
Et si je repars avec ma caméra, bah le vecteur vitesse augmente et donc je vais sampler dans des buffers avec des trainées plus longues, c'est qui est normal. C'est ce qu'il se passe en réalité : tu n'as pas "immédiatement du motion blur" quand tu bouges ta caméra. 'fin si, mais si tu regardais au ralenti, il te faudrait quelques frames de démarrage avant d'avoir le full blur (puisque tu ne démarres pas ton mouvement à une vitesse X mais tu accélères pour atteindre cette vitesse).

L'inconvénient 2 que je vois c'est que les méthodes a feedback c'est pas très cool aux bordures et ca sera dur d'avoir un résultat indépendant du FPS (cf ce thread) en plus si les couleurs sont pas dans un espace linéaire ça se complique.
@lx a raison car ce que tu voudrais approcher c'est une intégrale parfaite, Zavie fait 3 assomptions pour l'approcher et éviter d'avoir à sampler temporellement. Plus tu rajoute de buffers, plus ton retard de phase aux plus gros "niveaux" va augmenter.

Mais pourquoi ça merderait aux bordures ? Au contraire : tu samples pas le long d'un champ de vecteurs mais tu fais la somme des pixels au même endroit. C'est ce qui se passe avec une vraie caméra donc ça devrait être plus conforme à la réalité qu'aller sampler dans une direction pour approximer l'intégrale temporelle...

Ouais par contre pour les changements de framerate, là chuis d'accord : ça va être la marde ! ;D

'fin bon, y a qu'une façon d'être certains hein ! Tester !!! ;D
Malheureusement moi chuis reparti sur un job à la con qui va me mobiliser quasiment tout mon temps, finies les vacances ! (hé ouais, j'étais en vacances pendant 1 mois et demi, c'est pour ça que je pissais 15 trucs par semaine, mais c'est fini  :-[)
.  Pom  .

Offline Zavie

Re : Un article sur le flou de mouvement
« Reply #10 on: 12 January 2011 à 15:00:57 »
Juste pour spéculer : ça ferait quoi à ton avis si on utilisait un algo de ce genre :
[...]
C'est juste une idée comme ça, je sais pas si ça peut marcher vu que j'ai jamais fait de motion blur. Mais du coup avec ce genre d'algo, ça fait pas beaucoup d'opérations et ça devrait prendre en compte les artefacts de la boule rouge dans ton application, puisqu'elle sera ajoutée dans le calcul.

Je ne suis pas certain du résultat que ça donnerait, mais dis-nous si tu testes. :)
En tout cas là tu reviens vers la technique de blending avec les images précédentes : la boule rouge sera effectivement prise en compte et tu n'auras plus la plupart des problèmes que je signale, par contre les traînées ne seront plus homogènes il me semble (à vérifier) et le flou sera dépendant des images précédentes. Ça veut dire qu'il sera dépendant de la vitesse de rendu d'une part, et qu'il ne sera pas possible de faire un flou "en pause" d'autre part.
On ne l'exploite pas dans E, mais on pourrait très bien geler ou simplement ralentir la scène tout en gardant le flou, et éventuellement balader la caméra autour façon bullet time, ce qui est un effet qui m'intéresse.


Il me semble avoir déjà vu un truc dans le genre de la technique de Zavie, mais avec un twist en plus, un papier d'ATI il me semble. Le rendu du velocity buffer est fait à part et avec un vertex shader spécial qui déforme le mesh rendu en fonction de son mouvement : [...]

Ah ce n'est pas bête. Par contre je doute que ça se comporte bien pour autre chose qu'un mesh relativement limité dans l'espace (un personnage dans l'exemple de l'article).

Quote from: MsK`
Sinon niveau motion blur super cheaté, y'a la technique valve : tu blur l'image uniquement en fonction du mouvement de la caméra. Le rendu est excellent, les performances sont la, mais bien sur, si un objet bouge à l'écran et toi non, pas de blur dessus ^^

Oui c'est assez intuitif, et ça évite d'avoir à garder un moyen de retrouver l'ancienne position des objets. Ironiquement, ça aurait suffit dans E puisque la scène est statique.


Bon sinon prochaines étapes en ce qui me concerne : essayer éventuellement avec un centre instantané de rotation, et voir si un filtrage anisotropique ne permettrait pas d'avoir un flou de même qualité avec moins de fetches et un rendu plus rapide.

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Un article sur le flou de mouvement
« Reply #11 on: 14 February 2011 à 20:22:05 »
J'ai une question supplémentaire vis-à-vis de ton motion blur : tu n'en parles pas dans l'article mais comment tu traites la vitesse angulaire ?

Là je suis coincé car je calcule ma vitesse angulaire comme une crotte : j'ai complètement oublié qu'un objet ne tourne pas forcément autour d'un pivot centré en (0,0,0) et du coup il faudrait qu'à partir de mes 2 matrices à t et t-dt, je trouve un moyen de calculer le pivot. Une idée ? (j'y ai pas du tout réfléchi hein, je pose la question d'avance, pour voir si quelqu'un a déjà fait le calcul pendant que je m'y mets de mon côté ;D)
.  Pom  .

Offline Zavie

Re : Un article sur le flou de mouvement
« Reply #12 on: 16 February 2011 à 14:25:29 »
En fait j'en parle en disant que justement je la néglige : le flou est linéaire. Du coup ça ne marche vraisemblablement pas pour les petits éléments tournant rapidement (je n'ai pas vérifié, mais j'imagine que ça doit baver vers l'extérieur).

Pour tenir compte de la vitesse angulaire, ce que j'envisageais était de déterminer un centre instantané de rotation. Mais je ne suis pas certain que l'on puisse obtenir un centre crédible avec deux matrices. Avec trois matrices ça marcherait probablement, mais ça commence à faire beaucoup. Cela dit il doit y avoir moyen d'utiliser une information différente, plus pertinente.

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Un article sur le flou de mouvement
« Reply #13 on: 17 February 2011 à 17:25:22 »
Yo !

Bon alors après maints efforts et des bugs sournois de partout, j'y suis arrivé !
J'arrive à déterminer 3 paramètres critiques à partir de 2 matrices :
 _ La vitesse linéaire
 _ La vitesse angulaire
 _ Le point de pivot

Contrairement à Zavie, je calcule tout en WORLD space, mes vitesses sont donc stockées en WORLD.

Voici le code C++ que vous pouvez utiliser pour calculer ces 3 infos :
Code: [Select]
/// <summary>
/// Helper method that helps to compute the difference in translation and rotation of an object moving from one frame to the other
/// </summary>
/// <param name="_Previous">The object's matrix at previous frame</param>
/// <param name="_Current">The object's matrix at current frame</param>
/// <param name="_DeltaPosition">Returns the difference in position from last frame</param>
/// <param name="_DeltaRotation">Returns the difference in rotation from last frame</param>
/// <param name="_Pivot">Returns the pivot position the object rotated about</param>
public static void ComputeObjectDeltaPositionRotation( ref Matrix _Previous, ref Matrix _Current, out Vector3 _DeltaPosition, out Quaternion _DeltaRotation, out Vector3 _Pivot )
{
// Compute the rotation the matrix sustained
Quaternion PreviousRotation = QuatFromMatrix( _Previous );
Quaternion CurrentRotation = QuatFromMatrix( _Current );
_DeltaRotation = QuatMultiply( QuatInvert( PreviousRotation ), CurrentRotation );

Vector3 PreviousPosition = (Vector3) _Previous.Row4;
Vector3 CurrentPosition = (Vector3) _Current.Row4;

// Retrieve the pivot point about which that rotation occurred
float RotationAngle = _DeltaRotation.Angle;
if ( Math.Abs( RotationAngle ) > 1e-4f )
{
Vector3 RotationAxis = _DeltaRotation.Axis;
_Pivot = PreviousPosition;
Vector3 Previous2Current = CurrentPosition - PreviousPosition;
float L = Previous2Current.Length();
if ( L > 1e-4f )
{
Previous2Current /= L;
Vector3 N = Vector3.Cross( Previous2Current, RotationAxis );
N.Normalize();

Vector3 MiddlePoint = 0.5f * (PreviousPosition + CurrentPosition);
float Distance2Pivot = 0.5f * L / (float) Math.Tan( 0.5f * RotationAngle );
_Pivot = MiddlePoint + N * Distance2Pivot;
}

// Rotate previous position about pivot, this should yield us current position
Vector3 RotatedPreviousPosition = RotateAbout( PreviousPosition, _Pivot, _DeltaRotation );

// Close the gap so we have node delta translation
PreviousPosition = CurrentPosition;
}
else
_Pivot = Vector3.Zero;

_DeltaPosition = CurrentPosition - PreviousPosition; // Easy !
}

static Quaternion QuatFromMatrix( Matrix M )
{
Quaternion q = new Quaternion();

float s = (float) System.Math.Sqrt( M.M11 + M.M22 + M.M33 + 1.0f );
q.W = s * 0.5f;
s = 0.5f / s;
q.X = (M.M32 - M.M23) * s;
q.Y = (M.M13 - M.M31) * s;
q.Z = (M.M21 - M.M12) * s;

return q;
}

static Quaternion QuatInvert( Quaternion q )
{
float fNorm = q.LengthSquared();
if ( fNorm < float.Epsilon )
return q;

float fINorm = -1.0f / fNorm;
q.X *=  fINorm;
q.Y *=  fINorm;
q.Z *=  fINorm;
q.W *= -fINorm;

return q;
}

static Vector3 RotateAbout( Vector3 _Point, Vector3 _Pivot, Quaternion _Rotation )
{
Quaternion Q = new Quaternion( _Point - _Pivot, 0.0f );
Quaternion RotConjugate = _Rotation;
RotConjugate.Conjugate();
Quaternion Pr = QuatMultiply( QuatMultiply( _Rotation, Q ), RotConjugate );
Vector3 Protated = new Vector3( Pr.X, Pr.Y, Pr.Z );
return _Pivot + Protated;
}

static Quaternion QuatMultiply( Quaternion q0, Quaternion q1 )
{
Quaternion q;
Vector3 V0 = new Vector3( q0.X, q0.Y, q0.Z );
Vector3 V1 = new Vector3( q1.X, q1.Y, q1.Z );
q.W = q0.W * q1.W - Vector3.Dot( V0, V1 );
Vector3 V = q0.W * V1 + V0 * q1.W + Vector3.Cross( V0, V1 );
q.X = V.X;
q.Y = V.Y;
q.Z = V.Z;

return q;
}


On obtient la rotation effectuée par la matrice entre t et t-dt (i.e. la vitesse angulaire) en faisant :
   Qr = Qp^-1 * Qc (1)
avec:
   Qp^-1 l'inverse du quaternion de la matrice de la frame précédente
   Qc le quaternion de la matrice courante.

Pour ceux qu'aiment pas les quaternions, c'est exactement la même chose que de résoudre :
Mp * R = Mc
avec
   Mc votre matrice courante,
   Mp votre matrice précédente
   R la matrice de rotation, inconnue, qu'on cherche à trouver.

En composant à gauche par Mp^-1, l'inverse de la matrice précédente, on peut ré-écrire cette équation de cette manière :
   R = Mp^-1 * Mc (2)

Convertissez (2) en quaternions à la place des matrices et vous obtenez (1). Simple !

Bon maintenant c'est bien beau, on sait de combien la matrice a tourné mais le plus important est bien entendu de déterminer le point de pivot !
(comme un con je l'avais tout simplement oublié au début, du coup ça marchait bien pour les objets qui tournent sur eux-mêmes mais pas du tout pour ma caméra, qui tourne autour d'une target !)

Le calcul est relativement simple, on connaît :
   P(t-dt), la position de la matrice à la frame précédente
   P(t), la position de la matrice à la frame courante
   θ, l'angle de rotation
   R, l'axe de rotation

Si vous regardez le schéma suivant, on doit en déduire la position du point de pivot :


On connaît également :
   d = ||P(t) - P(t-dt)||
   P(t-dt/2) = 0.5 * [P(t) + P(t-dt)]
   N = [P(t) - P(t-dt)] ^ R  (à normaliser)

On en déduit, grace à de la trigonométrie de 4ème, que D = 1/2 * d / tan( θ/2 ), la distance de P(t-dt/2) au pivot.
Et finalement, Pivot = P(t-dt/2) + D * N

Je pensais que j'allais être emmerdé par des problèmes de précisions, genre mouvements brusques et autres d'une frame à l'autre mais apparemment ça se comporte assez bien ! ;D



Pour utiliser tout ça, c'est simple aussi (tant mieux, vu qu'il faut le faire pour chaque vertex !) :
   P' = P + Vl + (RotateAboutAxis( P, Pivot, Va ) - P)
avec :
   P la position originale (en WORLD)
   P' la nouvelle position après calcul (également en WORLD)
   Vl notre vitesse linéaire
   Va notre vitesse angulaire (quaternion)
   Pivot notre point de pivot magique

RotateAboutAxis() est une fonction qui applique une rotation du point P relativement au point de pivot, autour d'un axe par un angle donnés.
J'ai utilisé la formule de rotation donnée ici http://local.wasp.uwa.edu.au/~pbourke/geometry/rotate/ :

Point à rotater, converti en quaternion : Q1 = (WorldPos.X - Pivot.X, WorldPos.Y - Pivot.Y, WorldPos.Z - Pivot.Z, 0)
Rotation (notre quaternion de vitesse angulaire qu'on a déjà) : Q2 = (rx sin(θ/2), ry sin(θ/2), rz sin(θ/2), cos(θ/2))
Et : Q3 = Q2 Q1 Q2*
Plus qu'à récupérer le point rotaté dans XYZ du quaternion Q3, auquel on rajoute Pivot pour se retrouver en WORLD non offseté.

Et donc, notre vitesse finale, en WORLD, qu'on va écrire dans le buffer de vitesses pour la frame courante est simplement : V = P' - P
NOTE : On perd l'info de vitesse angulaire en écrivant cette vitesse dans le buffer, malheureusement. Mais pas pour la caméra, comme on le verra + loin !


Voici le code HLSL qui fait les calculs au runtime :
Code: [Select]
// Holds the method to compute the velocity of a WORLD position given the relative motion and rotation of the vertex's matrix
//
float3 DeltaPosition : MOTION_DELTA_POSITION; // Contains the WORLD previous->current matrix translation
float4 DeltaRotation : MOTION_DELTA_ROTATION; // Contains the WORLD previous->current matrix rotation (in the form of a quaternion)
float3 DeltaPivot : MOTION_DELTA_PIVOT; // Contains the WORLD pivot point about which the object rotated

float4 QuatProduct( float4 _Q0, float4 _Q1 )
{
return float4( (_Q0.w * _Q1.xyz) + (_Q0.xyz * _Q1.w) + cross( _Q0.xyz, _Q1.xyz ), (_Q0.w * _Q1.w) - dot( _Q0.xyz, _Q1.xyz ) );
}

// Rotates a point using a quaternion
// Maths are simple :
// 1) We form a quaternion Q = {Px,Py,Pz,0} from the point to rotate
// 2) We rotate Q using our rotation quaternion R by writing Q' = R P R* (R* being the conjugate of R)
//
float3 RotateAbout( float3 _Point, float3 _Pivot, float4 _R )
{
float4 Q = float4( _Point - _Pivot, 0.0 );
float4 Rc = float4( -_R.xyz, _R.w );
return _Pivot + QuatProduct( QuatProduct( _R, Q ), Rc ).xyz;
}

// Computes the velocity of a WORLD position given the delta position/rotation sustained by this point's LOCAL=>WORLD matrix
// _WorldPosition, the position of a point in its WORLD space
// _ObjectDeltaPosition, the delta position of the object (in WORLD space)
// _ObjectDeltaRotation, the delta rotation of the object (in WORLD space)
// _ObjectDeltaPivot, the rotation pivot of the object matrix (in WORLD space)
//
float3 ComputeVelocity( float3 _WorldPosition, float3 _ObjectDeltaPosition, float4 _ObjectDeltaRotation, float3 _ObjectDeltaPivot )
{
float3 Velocity = _ObjectDeltaPosition; // Really simple for translation : just add !

// Compute the delta-rotation matrix from the delta-rotation quaternion
float3 RotatedPosition = RotateAbout( _WorldPosition, _ObjectDeltaPivot, _ObjectDeltaRotation );

// Angular velocity is simply the difference between current and rotated position
Velocity += RotatedPosition - _WorldPosition;

return Velocity;
}

// Same but uses variables declared at the top
float3 ComputeVelocity( float3 _WorldPosition )
{
return ComputeVelocity( _WorldPosition, DeltaPosition, DeltaRotation, DeltaPivot );
}


Pour utiliser ce code, on doit calculer pour chaque objet sa vitesse linéaire, angulaire et le pivot en faisant :
Code: [Select]
Vector3 DeltaPosition, DeltaPivot;
Quaternion DeltaRotation;
ComputeObjectDeltaPositionRotation( ref PreviousLocal2World, ref CurrentLocal2World, out DeltaPosition, out DeltaRotation, out DeltaPivot );
Qu'on balance au shader, pour chaque objet.

Ensuite, le code HLSL pour calculer un vecteur vitesse WORLD dans un vertex shader quelconque :
Code: [Select]
float3 WorldVelocity = ComputeVelocity( WorldPosition ); // A passer au pixel shader et à écrire tel quel dans le buffer de vitesses !



Et finalement, pour la passe de motion blur, je calcule la vitesse linéaire, angulaire et le pivot de la caméra (comme pour n'importe quel autre objet) :
Code: [Select]
Vector3 DeltaPosition, DeltaPivot;
Quaternion DeltaRotation;
ComputeObjectDeltaPositionRotation( ref PreviousCamera2World, ref CurrentCamera2World, out DeltaPosition, out DeltaRotation, out DeltaPivot );

Que je balance au shader de motion blur, dont voici le code :
Code: [Select]

float4 PS( VS_IN _In ) : SV_TARGET0
{
// Read color, velocity & depth of the current pixel
float2 UV = _In.Position.xy * GBufferInvSize.xy;
float3 RGB = GBufferTexture0.SampleLevel( NearestClamp, UV, 0 ).xyz;
float3 WorldVelocity = GBufferTexture3.SampleLevel( NearestClamp, UV, 0 ).xyz;
float Depth = ReadDepth( UV );

// Rebuild WORLD position from depth
float3 View = float3( CameraData.y * CameraData.x * (2.0 * UV.x - 1.0), CameraData.x * (1.0 - 2.0 * UV.y), 1.0 );
float3 CameraPosition = View * Depth;
float3 WorldPosition = mul( float4( CameraPosition, 1.0 ), Camera2World ).xyz;

// Add camera velocity to the pixel's own velocity
WorldVelocity += ComputeVelocity( WorldPosition );

// Project into 2D
float4 VelocityProj = mul( float4( WorldPosition + WorldVelocity, 1.0 ), World2Proj );
VelocityProj /= VelocityProj.w;
float2 Velocity = 0.5 * float2( 1.0 + VelocityProj.x, 1.0 - VelocityProj.y ) - UV;

// Perform blur
float2 dUV = -BlurSize * Velocity / STEPS_COUNT; // <= J'utilise 16 steps
float SumWeights = 1.0;
for ( int StepIndex=0; StepIndex < STEPS_COUNT; StepIndex++ )
{
UV += dUV;
RGB += GBufferTexture0.SampleLevel( NearestClamp, UV, 0 ).xyz;
SumWeights += 1.0;
}

RGB *= BlurWeightFactor / SumWeights;

return float4( RGB, 1.0 );
}

Voici le résultat quand je tourne la caméra autour d'une target dont on aperçoit bien le centre, grace à super-pivot ;D :

(les trainées super énormes et rectilignes sur les particouilles qui sont sur fond noir, pas celles qui ont un mesh derrière elles, viennent du fait que le Z du pixel se retrouve à l'infini et on perçoit donc les particules comme étant à l'infini également, ce qui est une limitation de tous les motion blur cheapos quoi qu'il arrive)


Voici 2 images avec un seul objet tournant très rapidement (une boîte avec 2 boîtes filles attachées) :
SANS motion bleurre

AVEC motion bleurre

C'est évidemment pas parfait car les points en dehors des boîtes ont une vitesse angulaire nulle et la trainée de blur s'arrête brutalement. Il faudrait "étendre" le mesh d'une manière quelconque je pense. Peut-être l'afficher 2 fois : une fois à la position de la frame précédente grace à notre calcul de vitesse, et une seconde fois à sa position actuelle tel qu'on le fait normalement ?
Ou bien p'têt calculer une silhouette qu'on extruderait en suivant la vitesse ? Il me semble que j'ai déjà vu ce truc dans un exemple DirectX avec un vieux lion super moche, j'avais trouvé ça bien naze à l'époque...
Idéalement, il faudrait savoir où se trouve le centre de l'objet et extruder depuis ce centre je pense (je pourrais essayer avec mon pivot comme centre tiens, huhu !).


A noter 2 trucs que j'ai pas faits :
   1) Limiter la vitesse projetée pour pas que ça bave de trop
   2) Me servir du point de pivot et de la vitesse angulaire de la caméra pour calculer un arc de cercle, que je projetterais en 2D et que je suivrais, à la place de suivre une trajectoire bêtement rectiligne

Pour 2), je pensais simplement à projeter 3 points de l'arc de cercle en 2D : le point de départ, le point d'arrivée et le point milieu. J'approximerais ces 3 points par une parabole et l'inner loop de motion blur deviendrait donc :

Code: [Select]
float2 dUV = Some magic delta computed from the projected parabola
float2 ddUV = Some magic delta velocity also computed from the projected parabola
float SumWeights = 1.0;
for ( int StepIndex=0; StepIndex < STEPS_COUNT; StepIndex++ )
{
UV += dUV; // <== Vitesse
dUV += ddUV; // <== Accélération
RGB += GBufferTexture0.SampleLevel( NearestClamp, UV, 0 ).xyz;
SumWeights += 1.0;
}

RGB *= BlurWeightFactor / SumWeights;

return float4( RGB, 1.0 );
}

A creuser, mais chuis pas certain que ça se voie tant que ça d'interpoiler sur un arc plutôt qu'une ligne...
.  Pom  .

Offline RaHoW

  • Base
    • Pouet.net
    • View Profile
    • Apex - official site
  • Ancienneté: 1993
  • Groupe: Apex
  • Rôle: Gfx, code, orga
Re : Un article sur le flou de mouvement
« Reply #14 on: 17 February 2011 à 22:13:56 »
Arf ^^ On peut t'appeler "Pata-ouf" ?? ^______-
=RaHoW/Apex=