Pourquoi utiliser Time.deltaTime dans les fonctions Lerping?

12

À ma connaissance, une fonction Lerp interpole entre deux valeurs ( aet b) en utilisant une troisième valeur ( t) entre 0et 1. À t = 0, la valeur a est renvoyée, à t = 1, la valeur best renvoyée. À 0,5, la valeur à mi-chemin entre aet best renvoyée.

(L'image suivante est une étape lisse, généralement une interpolation cubique)

entrez la description de l'image ici

J'ai parcouru les forums et sur cette réponse, j'ai trouvé la ligne de code suivante:transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime);

Je me suis dit: "quel idiot, il n'a aucune idée" mais comme il y avait plus de 40 votes positifs, j'ai essayé et bien sûr, cela a fonctionné!

float t = Time.deltaTime;
transform.rotation = Quaternion.Slerp(transform.rotation, toRotation, t);
Debug.Log(t);

J'ai obtenu des valeurs aléatoires entre 0.01et 0.02pour t. La fonction ne devrait-elle pas interpoler en conséquence? Pourquoi ces valeurs se cumulent-elles? Qu'est-ce que le lerp que je ne comprends pas?

AzulShiva
la source
1
A est généralement la position, qui change et, par conséquent, un échantillonnage à 1/60 (60 ips) ne déplacerait l'objet que par interpolation de 0,16, ce qui rétrécit continuellement la distance entre A et B (ainsi l'échantillon est de plus en plus petit à chaque fois).
Sidar
Vous avez connecté t et lerpé avec tt ... ce sont différentes variables.
user253751

Réponses:

18

Voir aussi cette réponse .

Il existe deux façons courantes d'utiliser Lerp:

1. Mélange linéaire entre un début et une fin

progress = Mathf.Clamp01(progress + speedPerTick);
current = Mathf.Lerp(start, end, progress);

C'est la version que vous connaissez probablement le mieux.

2. Facilité exponentielle vers une cible

current = Mathf.Lerp(current, target, sharpnessPerTick);

Notez que dans cette version, la currentvaleur apparaît à la fois comme sortie et comme entrée. Il déplace la startvariable, nous partons donc toujours de l'endroit où nous nous sommes déplacés lors de la dernière mise à jour. C'est ce qui donne cette version d' Lerpune mémoire d'une image à l'autre. À partir de ce point de départ en mouvement, nous déplaçons ensuite une fraction de la distance vers celle targetdictée par un sharpnessparamètre.

Ce paramètre n'est plus tout à fait une "vitesse", car nous approchons la cible d'une manière zéno . Si tel sharpnessPerTickétait le cas 0.5, lors de la première mise à jour, nous irions à mi-chemin de notre objectif. Ensuite, lors de la prochaine mise à jour, nous déplacerions la moitié de la distance restante (donc un quart de notre distance initiale). Ensuite, le lendemain, nous bougions à nouveau de moitié ...

Cela donne une "facilité exponentielle" où le mouvement est rapide lorsqu'il est loin de la cible et ralentit progressivement à mesure qu'il approche asymptotiquement (bien qu'avec des nombres de précision infinie, il ne l'atteindra jamais dans un nombre fini de mises à jour - pour nos besoins, il se rapproche suffisamment). C'est idéal pour chasser une valeur cible mobile ou lisser une entrée bruyante en utilisant une " moyenne mobile exponentielle ", en utilisant généralement un très petit sharpnessPerTickparamètre comme 0.1ou plus petit.


Mais vous avez raison, il y a une erreur dans la réponse votée que vous liez. Ce n'est pas corriger pour deltaTimela bonne façon. Il s'agit d'une erreur très courante lors de l'utilisation de ce style de Lerp.

Le premier style de Lerpest linéaire, nous pouvons donc ajuster linéairement la vitesse en multipliant par deltaTime:

progress = Mathf.Clamp01(progress + speedPerSecond * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);

Mais notre assouplissement exponentiel n'est pas linéaire , donc multiplier simplement notre sharpnessparamètre par deltaTimene donnera pas la correction temporelle correcte. Cela apparaîtra comme un tremblement dans le mouvement si notre cadence fluctue, ou un changement dans la netteté de l'assouplissement si vous passez de 30 à 60 de manière cohérente.

Au lieu de cela, nous devons appliquer une correction exponentielle pour notre facilité exponentielle:

blend = 1f - Mathf.Pow(1f - sharpness, Time.deltaTime * referenceFramerate);
current = Mathf.Lerp(current, target, blend);

Voici referenceFrameratejuste une constante comme 30garder les unités pour sharpnessla même chose que nous utilisions avant de corriger le temps.


Il y a une autre erreur discutable dans ce code, qui utilise Slerp- l'interpolation linéaire sphérique est utile lorsque nous voulons un taux de rotation exactement cohérent tout au long du mouvement. Mais si nous allons utiliser une facilité exponentielle non linéaire de toute façon, Lerpcela donnera un résultat presque impossible à distinguer et c'est moins cher. ;) Les quaternions lerp beaucoup mieux que les matrices, c'est donc généralement une substitution sûre.

DMGregory
la source
1

Je pense que le concept de base manquant serait dans ce scénario A n'est pas fixe. A est mis à jour à chaque étape, quelle que soit l'interpolation de Time.deltaTime.

Ainsi, avec A se rapprochant de B à chaque étape, l'espace total de l'interpolation change avec chaque appel Lerp / Slerp. Sans faire le calcul, je soupçonne que l'effet n'est pas le même que votre graphique Smoothstep, mais est un moyen bon marché d'approximer une décélération lorsque A se rapproche de B.

En outre, cela est fréquemment utilisé car B peut ne pas être statique non plus. Le cas typique peut être une caméra qui suit un joueur. Vous voulez éviter les secousses, en faisant sauter la caméra vers un emplacement ou une rotation.

Chris
la source
1

Vous avez raison, la méthode Quaternion Slerp(Quaternion a, Quaternion b, float t)interpole entre aet bpar le montant t. Mais regardez la première valeur, ce n'est pas la valeur de départ.

Ici, la première valeur donnée à la méthode est la rotation actuelle de l'objet transform.rotation. Ainsi, pour chaque image, il interpole entre la rotation actuelle et la rotation cible _lookRotationde la quantité Time.deltaTime.

C'est pourquoi il produit une rotation douce.

Ludovic Feltz
la source
2
Maintenant, je me sens idiot
AzulShiva
@AzulShiva Ne vous inquiétez pas, cela arrive à tout le monde;)
Ludovic Feltz