Perché usare Time.deltaTime nelle funzioni di Lerping?


12

Per quanto ne so, una funzione di Lerp interpola tra due valori ( ae b) usando un terzo valore ( t) tra 0e 1. At t = 0, viene restituito t = 1il valore a , at , bviene restituito il valore . A 0,5 viene restituito il valore a metà tra ae b.

(L'immagine seguente mostra una sequenza graduale, generalmente un'interpolazione cubica)

inserisci qui la descrizione dell'immagine

Ho visitato i forum e su questa risposta ho trovato la seguente riga di codice:transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime);

Pensavo tra me e me "che sciocco, non ne ha idea" ma dato che aveva più di 40 voti, l'ho provato e abbastanza sicuro, ha funzionato!

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

Ho ottenuto valori casuali tra 0.01e 0.02per t. La funzione non dovrebbe interpolare di conseguenza? Perché questi valori si accumulano? Cosa c'è di lerp che non capisco?


1
A è di solito posizione, che cambia e quindi il campionamento a 1/60 (60 fps) sposterebbe l'oggetto solo per interpolazione di 0,16 restringendo continuamente la distanza tra A e B (quindi il campione è sempre più piccolo ogni volta).
Sidar,

Hai registrato t e lerped con tt ... quelle sono variabili diverse.
user253751,

Risposte:


18

Vedi anche questa risposta .

Esistono due modi comuni per utilizzare Lerp:

1. Fusione lineare tra un inizio e una fine

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

Questa è la versione con cui probabilmente hai più familiarità.

2. Facilità esponenziale verso un bersaglio

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

Si noti che in questa versione il currentvalore appare sia come output sia come input. Sposta la startvariabile, quindi iniziamo sempre da dove ci siamo spostati sull'ultimo aggiornamento. Questo è ciò che dà questa versione di Lerpuna memoria da un frame all'altro. Da questo punto di partenza mobile, spostiamo quindi una frazione della distanza verso il targetdettato da un sharpnessparametro.

Questo parametro non è più una "velocità", perché ci avviciniamo al bersaglio in modo simile a Zenone . Se lo sharpnessPerTickfosse 0.5, al primo aggiornamento ci sposteremmo a metà del nostro obiettivo. Quindi al prossimo aggiornamento avremmo spostato metà della distanza rimanente (quindi un quarto della nostra distanza iniziale). Poi il prossimo ci sposteremmo di nuovo la metà ...

Ciò fornisce un "rilassamento esponenziale" in cui il movimento è veloce quando lontano dall'obiettivo e rallenta gradualmente mentre si avvicina in modo asintotico (sebbene con numeri a precisione infinita non lo raggiungerà mai in nessun numero finito di aggiornamenti - per i nostri scopi esso si avvicina abbastanza). È ottimo per inseguire un valore target mobile o per livellare un input rumoroso usando una " media mobile esponenziale ", di solito usando un sharpnessPerTickparametro molto piccolo come 0.1o più piccolo.


Ma hai ragione, c'è un errore nella risposta votata che colleghi. Non si corregge deltaTimenel modo giusto. Questo è un errore molto comune quando si utilizza questo stile di Lerp.

Il primo stile di Lerpè lineare, quindi possiamo regolare linearmente la velocità moltiplicando per deltaTime:

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

Ma il nostro allentamento esponenziale non è lineare , quindi moltiplicare il nostro sharpnessparametro per deltaTimenon darà la corretta correzione temporale. Questo apparirà come un giudice nel movimento se il nostro framerate fluttua o un cambiamento nella nitidezza di attenuazione se passi costantemente da 30 a 60.

Invece dobbiamo applicare una correzione esponenziale per la nostra facilità esponenziale:

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

Ecco referenceFramerateuna costante come 30mantenere le unità per sharpnesslo stesso che stavamo usando prima di correggere il tempo.


C'è un altro errore discutibile in quel codice, che sta usando Slerp: l'interpolazione lineare sferica è utile quando vogliamo una velocità di rotazione esattamente coerente attraverso l'intero movimento. Ma se Lerputilizzeremo comunque una facilità esponenziale non lineare, si otterrà un risultato quasi indistinguibile ed è più economico. ;) I quaternioni sono molto meglio delle matrici, quindi di solito questa è una sostituzione sicura.


1

Penso che il concetto di base mancante sarebbe in questo scenario A non risolto. A viene aggiornato ad ogni passaggio, per quanto lungo l'interpolazione di Time.deltaTime.

Quindi, con A che si avvicina a B ad ogni passo, lo spazio totale dell'interpolazione cambia con ogni chiamata Lerp / Slerp. Senza fare i conti, sospetto che l'effetto non sia lo stesso del grafico Smoothstep, ma è un modo economico per approssimare una decelerazione quando A si avvicina a B.

Inoltre, questo viene spesso usato perché B potrebbe non essere neanche statico. Caso tipico potrebbe essere una telecamera che segue un lettore. Volete evitare il jerkiness, facendo saltare la telecamera in una posizione o rotazione.


1

Hai ragione, il metodo si Quaternion Slerp(Quaternion a, Quaternion b, float t)interpola tra ae bper l'importo t. Ma guarda il primo valore, non è il valore iniziale.

Qui il primo valore dato al metodo è la rotazione dell'oggetto corrente transform.rotation. Quindi per ogni fotogramma interpola tra la rotazione corrente e la rotazione target _lookRotationper la quantità Time.deltaTime.

Ecco perché produce una rotazione regolare.


2
Ora mi sento idiota
AzulShiva,

@AzulShiva Non ti preoccupare, succede a tutti;)
Ludovic Feltz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.