Эта статья продолжает серию статей о WebGL. Первая была об основах, а в предыдущей мы рассмотрели камеры. Если вы их ещё не читали, то рекомендую сначала ознакомиться с ними.
Как создать анимацию в WebGL?
На самом деле анимация - это больше работа JavaScript, нежели WebGL. Для анимации необходимо менять что-либо в JavaScript с течением времени и при этом постоянно вызывать отрисовку.
Мы можем взять один из наших предыдущих примеров и сделать анимацию:
*var fieldOfViewRadians = degToRad(60);
*var rotationSpeed = 1.2;
*requestAnimationFrame(drawScene);
// отрисовка сцены
function drawScene() {
* // каждый кадр немного увеличивается поворот
* rotation[1] += rotationSpeed / 60.0;
...
* // в следующем кадре снова вызываем drawScene
* requestAnimationFrame(drawScene);
}
И анимация готова:
Однако, здесь есть одна неявная проблема. В коде выше содержится
rotationSpeed / 60.0
. Мы делим на 60.0, потому что предполагаем, что браузер
вызовет requestAnimationFrame 60 раз за секунду, что является близким к правде.
Однако, это не верное предположение. Возможно, у пользователя медленное устройство вроде старого смартфона. Или, возможно, у пользователя в фоне запущена очень ресурсоёмкая программа. Есть множество причин, по которым браузер может не отображать 60 кадров в секунду. Возможно, наступил 2020 год и все устройства работают на частоте 240 кадров в секунду. А возможно, пользователь большой фанат игр и у него ЭЛТ-монитор с частотой 90 кадров в секунду.
Проблема наглядно продемонстрирована на следующем примере:
В примере выше мы хотели бы поворачивать все буквы 'F' с одной скоростью. Буква 'F' посередине работает на полной скорости и её частота кадров ни от чего не зависит. Буквы слева и справа симулируют, будто браузер работает на 1/8 от полной мощности компьютера. Буква слева ЗАВИСИМА от частоты кадров. Буква справа НЕЗАВИСИМА от частоты кадров
Заметьте, что буква слева не принимает во внимание, что частота кадров может быть низкой, и не успевает поворачиваться. Буква справа, даже несмотря на работу в режиме 1/8 доли от частоты кадров, успевает вращаться и не отстаёт от средней буквы, вращающейся на полной скорости.
Для того, чтобы сделать анимацию независимой от кадров, необходимо вычислять, сколько времени проходит между кадрами, и использовать это для определения времени анимации текущего кадра.
Для начала нам нужно получить время. К счастью, requestAnimationFrame
при своём
вызове передаёт нам время, прошедшее с загрузки страницы.
Лично мне кажется, что проще всего работать со временем в секундах, но так как
requestAnimationFrame
передаёт время в миллисекундах (тысяча секунд), нам нужно
умножить на 0.001, чтобы получить секунды.
После этого мы сможем получить разницу во времени.
*var then = 0;
requestAnimationFrame(drawScene);
// отрисовка сцены
*function drawScene(now) {
* // получаем время в секундах
* now *= 0.001;
* // вычитаем предыдущее значение времени от текущего
* var deltaTime = now - then;
* // запоминаем текущее время для следующего кадра
* then = now;
...
Когда у нас есть deltaTime
в секундах, наши расчёты будут строиться на том,
сколько раз в секунду мы хотим выполнять что-либо. В нашем случае значение
rotationSpeed
равно 1.2, то есть мы хотим выполнять поворот на 1.2 радиана
в секунду. Это примерно 1/5 поворота или, другими словами, нам необходимо 5
секунд для полного поворота независимо от частоты кадров.
* rotation[1] += rotationSpeed * deltaTime;
Вот рабочий пример.
Вероятно, вы не заметите разницы с примером в верху страницы, если только у вас не медленный компьютер. Но если не учитывать частоту кадров, у некоторых ваших пользователей картина может сильно отличаться от той, которую вы планировали.
Далее рассмотрим работу с текстурами.