W moim rozumieniu funkcja Lerp interpoluje dwie wartości ( a
i b
) przy użyciu trzeciej wartości ( t
) pomiędzy 0
i 1
. At t = 0
zwracana jest wartość a t = 1
, zwracana b
jest wartość . Przy 0,5 zwracana jest wartość w połowie między a
i b
.
(Poniższy obrazek przedstawia płynny krok, zwykle interpolację sześcienną)
Przeglądałem fora i na tej odpowiedzi znalazłem następujący wiersz kodu:transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime);
Pomyślałem sobie: „co za głupiec, on nie ma pojęcia”, ale ponieważ miał ponad 40 głosów pozytywnych, spróbowałem i na pewno zadziałało!
float t = Time.deltaTime;
transform.rotation = Quaternion.Slerp(transform.rotation, toRotation, t);
Debug.Log(t);
Mam losowe wartości pomiędzy 0.01
i 0.02
dla t
. Czy funkcja nie powinna interpolować odpowiednio? Dlaczego te wartości się stosują? Co takiego jest w Lerp, czego nie rozumiem?
źródło
Odpowiedzi:
Zobacz także tę odpowiedź .
Istnieją dwa typowe sposoby użycia
Lerp
:1. Liniowe mieszanie między początkiem a końcem
Jest to wersja, którą prawdopodobnie znasz.
2. Wykładnicza łatwość w kierunku celu
Zauważ, że w tej wersji
current
wartość pojawia się zarówno jako dane wyjściowe, jak i dane wejściowe. Wypierastart
zmienną, więc zawsze zaczynamy od miejsca, w którym przeprowadziliśmy się przy ostatniej aktualizacji. To daje tej wersjiLerp
pamięci od jednej klatki do drugiej. Następnie z tego ruchomego punktu początkowego przesuwamy ułamek odległości w kierunkutarget
podyktowanymsharpness
parametrem.Ten parametr nie jest już „szybkością”, ponieważ zbliżamy się do celu w sposób podobny do Zeno . Gdyby
sharpnessPerTick
były0.5
, to przy pierwszej aktualizacji przenieślibyśmy się w połowie drogi do celu. Następnie przy następnej aktualizacji przesunęlibyśmy się o połowę pozostałej odległości (czyli o jedną czwartą początkowej odległości). Następnie w następnym przejdziemy ponownie o połowę ...Daje to „wykładniczą ulgę”, w której ruch jest szybki, gdy daleko od celu, i stopniowo zwalnia, gdy zbliża się asymptotycznie (choć przy liczbach o nieskończonej precyzji nigdy go nie osiągnie w żadnej skończonej liczbie aktualizacji - dla naszych celów jest to wystarczająco blisko). Jest świetny do ścigania ruchomej wartości docelowej lub wygładzania głośnego sygnału wejściowego przy użyciu „ wykładniczej średniej ruchomej ”, zwykle przy użyciu bardzo małego
sharpnessPerTick
parametru, takiego jak0.1
lub mniejszego.Ale masz rację, w podanej wyżej linku znajduje się błąd. To nie poprawia
deltaTime
we właściwy sposób. Jest to bardzo częsty błąd podczas korzystania z tego styluLerp
.Pierwszy styl
Lerp
jest liniowy, więc możemy liniowo dostosować prędkość, mnożąc przezdeltaTime
:Ale nasze łagodzenie wykładnicze jest nieliniowe , więc pomnożenie naszego
sharpness
parametru przezdeltaTime
nie da poprawnej korekty czasu. Będzie to widoczne jako drżenie w ruchu, jeśli nasze tempo klatek będzie się zmieniać, lub zmiana łagodnej ostrości, jeśli konsekwentnie będziesz przechodził z 30 do 60.Zamiast tego musimy zastosować wykładniczą korektę dla naszej wykładniczej łatwości:
Oto
referenceFramerate
po prostu stałe30
zachowanie jednosteksharpness
tak, jak używaliśmy przed korektą czasu.W tym kodzie jest jeszcze jeden możliwy do uzasadnienia błąd
Slerp
- sferyczna interpolacja liniowa jest przydatna, gdy chcemy dokładnie spójnego tempa obrotu w całym ruchu. Ale jeśli i tak będziemy korzystać z nieliniowej łatwości wykładniczej,Lerp
da to prawie nie do odróżnienia wynik i jest tańszy. ;) Quaternions lerp są znacznie lepsze niż macierze, więc jest to zwykle bezpieczna zamiana.źródło
Myślę, że brakująca podstawowa koncepcja byłaby w tym scenariuszu A nie jest naprawiona. Wartość A jest aktualizowana z każdym krokiem, niezależnie od interpolacji, jaką ma Time.deltaTime.
Zatem, gdy A zbliża się do B z każdym krokiem, całkowita przestrzeń interpolacji zmienia się z każdym wywołaniem Lerp / Slerp. Podejrzewam, że bez robienia prawdziwej matematyki efekt nie jest taki sam jak na wykresie Smoothstep, ale jest tanim sposobem przybliżenia opóźnienia, gdy A zbliża się do B.
Jest to również często używane, ponieważ B może również nie być statyczny. Typowym przypadkiem może być kamera śledząca gracza. Chcesz uniknąć szarpnięć, skoku kamery w wybrane miejsce lub obrotu.
źródło
Masz rację, metoda
Quaternion Slerp(Quaternion a, Quaternion b, float t)
interpoluje pomiędzya
ib
według kwotyt
. Ale patrz pierwsza wartość, to nie jest wartość początkowa.Tutaj pierwszą wartością podaną dla metody jest bieżący obrót obiektu
transform.rotation
. Tak więc dla każdej ramki interpoluje między obrotem bieżącym a docelowym_lookRotation
o wartośćTime.deltaTime
.Dlatego zapewnia płynny obrót.
źródło