Навіщо використовувати Time.deltaTime у функціях Лерпінга?


12

Наскільки я розумію, функція Lerp інтерполює між двома значеннями ( aі b), використовуючи третє значення ( t) між 0і 1. At t = 0, значення a повертається, at t = 1, значення bповертається. На 0,5 значення на півдорозі між aі bповертається.

(На наступному малюнку - плавний крок, як правило, кубічна інтерполяція)

введіть тут опис зображення

Я переглядав форуми, і в цій відповіді знайшов такий рядок коду:transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime);

Я подумав собі: "який дурень, він поняття не має", але оскільки він мав 40+ оновлень, я спробував це і досить впевнено, він спрацював!

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

Я отримав випадкові значення між 0.01і 0.02за t. Чи не повинна функція відповідно інтерполюватись? Чому ці значення укладаються? Що з lerp я не розумію?


1
Зазвичай позиція A, яка змінюється, і для цього вибірки при 1/60 (60 кадрів в секунду) переміщують об'єкт лише інтерполяцією 0,16 безперервно звужуючи відстань між A і B (таким чином вибірка щоразу стає меншою і меншою).
Сидар

Ви входили в t і лерпували за допомогою tt ... це різні змінні.
користувач253751

Відповіді:


18

Дивіться також цю відповідь .

Є два поширених способи використання Lerp:

1. Лінійне поєднання між початком і кінцем

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

Це версія, яку ви, мабуть, найбільше знайомі.

2. Експоненціальна легкість до цілі

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

Зауважте, що в цій версії currentзначення відображається і як вихід, і як вхід. Він зміщує startзмінну, тому ми завжди починаємо з того місця, куди ми переїхали в останнє оновлення. Саме це дає цій версії Lerpпам’яті від одного кадру до іншого. З цієї рухомої початкової точки потім переміщуємо частину відстані до targetпродиктованого sharpnessпараметром.

Цей параметр вже не зовсім "швидкий", тому що ми наближаємось до цілі, як у Зенона . Якби вони sharpnessPerTickбули 0.5, то під час першого оновлення ми б рухалися на півдорозі до своєї мети. Тоді під час наступного оновлення ми перемістимо половину відстані, що залишилася (тобто чверть нашої початкової відстані). Тоді на наступному ми знову переїдемо наполовину ...

Це дає "експоненціальне полегшення", коли рух швидкий, коли знаходиться далеко від мети і поступово сповільнюється, коли він наближається асимптотично (хоча з нескінченною точністю чисел він ніколи не досягне його в будь-якій кінцевій кількості оновлень - для наших цілей це наближається досить). Він відмінно підходить для переслідування рухомого цільового значення або згладжування галасливого введення, використовуючи " експоненціальну ковзну середню ", зазвичай, використовуючи дуже невеликий sharpnessPerTickпараметр типу 0.1або менший.


Але ви маєте рацію, у оновленій відповіді, на яку ви посилаєтесь, є помилка. Це не виправлення deltaTimeправильного шляху. Це дуже поширена помилка при використанні цього стилю Lerp.

Перший стиль Lerpлінійний, тому ми можемо лінійно регулювати швидкість, множивши на deltaTime:

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

Але наше експоненціальне полегшення нелінійне , тому просто множення нашого sharpnessпараметра deltaTimeне дасть правильної корекції часу. Це відображатиметься як суддя в русі, якщо наша частота кадрів коливається, або зміна різкості ослаблення, якщо ви рухаєтесь від 30 до 60 послідовно.

Натомість нам потрібно застосувати експоненціальну корекцію для нашої експоненціальної легкості:

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

Ось referenceFramerateлише константа, як 30зберігати одиниці так sharpnessсамо, як ми використовували, перш ніж виправляти час.


У цьому коді є ще одна аргументована помилка, яка використовується Slerp- сферична лінійна інтерполяція є корисною, коли ми хочемо точно послідовної швидкості обертання через весь рух. Але якщо ми все одно Lerpбудемо використовувати нелінійну експоненціальну легкість, це дасть майже нерозрізний результат і дешевше. ;) Кватерніони лерп набагато краще, ніж матриці, тому зазвичай це безпечна заміна.


1

Я думаю, що основна концепція, яка відсутня, була б у цьому сценарії А, не зафіксована. A оновлюється з кожним кроком, настільки ж настільки, наскільки інтерполяція є Time.deltaTime

Отже, з наближенням до B з кожним кроком загальний простір інтерполяції змінюється з кожним викликом Lerp / Slerp. Не займаючись фактичною математикою, я б підозрював, що ефект не такий, як ваш графік Smoothstep, але це дешевий спосіб наблизити уповільнення, коли A наближається до B.

Крім того, це часто використовується, тому що B також не може бути статичним. Типовим випадком може бути камера, яка йде за гравцем. Ви хочете уникнути ривків, прискочивши камеру до місця чи обертання.


1

Ви маєте рацію, метод Quaternion Slerp(Quaternion a, Quaternion b, float t)інтерполює між aі bна суму t. Але дивіться перше значення, це не початкове значення.

Тут першим значенням, що надається методу, є поточне обертання об'єкта transform.rotation. Отже для кожного кадру він інтерполюється між поточним обертанням і цільовим обертанням _lookRotationна величину Time.deltaTime.

Ось чому він виробляє плавне обертання.


2
Тепер я відчуваю себе ідіотом
AzulShiva

@AzulShiva Не хвилюйся, що це трапляється з усіма;)
Людовик Фельц
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.