Я создал очень наивную страницу, которая будет брать сцену Three.js (хотя это может быть любая анимация, отличная от WebGL <canvas>
) и экспортировать отдельные изображения (или последовательности изображений) в затембыть преобразованным в видео.
Я делаю это частично как способ изучения Python, но также и возможность быстро прототипировать что-то в Three.js и затем экспортировать массивное шелковистое гладкое видео с высоким разрешением,супер привлекательный для меня.В прошлом я использовал программное обеспечение для захвата экрана, чтобы захватывать видео, хотя это всегда казалось немного неуклюжим, и падение FPS в реальном времени просвечивало и в финальном видео.
Процесс, который у меня сейчас есть, выглядит следующим образом:
Javascript :
- Создание сцены WebGL и настройка цикла рендеринга
- Установите ширину / высоту холста в соответствии с желаемыми размерами
- Визуализируйте сцену, используя
requestAnimationFrame
- Приостановите цикл рендеринга / обновления сцены
- Вызовите
toDataURL()
наэлемент canvas и извлекает строку base64 - POST-запрос к скрипту Python, передавая строку base64 (и другие вещи, например, целевой каталог для сохранения и захватывается ли отдельное изображение или последовательность)
Python:
- Обрезать тип содержимого заголовка MIME и декодировать строку base64
- Записать строку в файл изображения
- Распечатать / вернуть строку, которая означает успешное состояние, если файл был записан, в противном случае распечатать сообщение об ошибке
import base64, cgi, cgitb, datetime, glob, re, os</p>
<code>cgitb.enable()
#cgitb.enable(display=0, logdir='/tmp')
print "Content-type: text/html"
print
def main():
form = cgi.FieldStorage()
saveLocation = "../httpdocs/export/"
# POST variables
dataURL = form['dataURL'].value
captureSequence = form['captureSequence'].value
folderName = saveLocation + form['folderName'].value
saveImage(dataURL, captureSequence, saveLocation, folderName)
def saveImage(dataURL, captureSequence, saveLocation, folderName):
# strip out MIME content-type (e.g. "data:image/png;base64,")
dataURL = dataURL[dataURL.index(','):]
decodedString = base64.decodestring(dataURL)
if captureSequence == 'true':
# based off http://www.akeric.com/blog/?p=632
currentImages = glob.glob(folderName + "/*.jpg")
# TODO: perhaps filenames shouldnt start at %08d+1 but rather %08d+0?
numList = [0]
if not os.path.exists(folderName):
os.makedirs(folderName)
for img in currentImages:
i = os.path.splitext(img)[0]
try:
num = re.findall('[0-9]+$', i)[0]
numList.append(int(num))
except IndexError:
pass
numList = sorted(numList)
newNum = numList[-1] + 1
saveName = folderName + '/%08d.jpg' % newNum
else:
if not os.path.exists(saveLocation):
os.makedirs(saveLocation)
saveName = saveLocation + datetime.datetime.now().isoformat().replace(':', '.') + '.jpg'
# TODO; rather than returning a simple string, consider returning an object?
try:
output = open(saveName, 'w')
output.write(decodedString)
output.close()
print 'true'
except Exception, e:
print e
if __name__ == '__main__':
main()
</code>
Javascript :
- Получение ответа от скрипта Python и диспlay return message
- Возобновить / обновить цикл рендеринга
- (Повторить процесс столько раз, сколько нужно)
Это то, что я всегда запускаю локально, поэтомуне было бы риска конфликтующих записей или чего-то в этом роде.
Я провел несколько быстрых тестов, и, похоже, он работает по большей части, хотя и немного медленно.
- Я упускаю что-то совершенно очевидное в том, как это делается?Как это можно улучшить?( Особенно на стороне Python вещей ..)
- Неэффективно ли делать отдельный вызов ajax для изображения?Одним из преимуществ является то, что я могу просто остановить / закрыть вкладку в любое время, и она сохранит все изображения до этого момента.Будет ли какая-то польза от хранения всех этих строк base64 и отправки их в самом конце?
- Поскольку
requestAnimationFrame
ограничит цикл обновления до 60 кадров в секунду, можно ли легко установить более низкую частоту кадров?Допустим, по какой-то стилистической причине я хотел бы обновить все со скоростью 15 кадров в секунду, будет ли единственный вариант использовать setTimeout(callback, 1000 / targetFPS)
с пониманием того, что со временем он будет дрейфовать ? Исходя из вышеизложенного, эта анимация имеет переменную frame
, которая увеличивается на 1
каждый цикл обновления.Затем эта переменная используется для управления различными частями анимации (например, вращением куба и передачей вершинным / фрагментным шейдерам для управления цветами и координатами текстуры UV).
Если я хочу смоделировать что-то вроде 15fps, я был бы прав, если бы вместо этого я увеличил frame
на (60 / 15)
?Существует ли более элегантный способ легко переключаться между ограниченными частотами кадров?
- Наконец , существуют ли какие-либо методы, которые можно было бы использовать для улучшения качества визуализируемых изображений?(Размышляя вслух, экономя в двойном размере, а затем уменьшая их?)
Я действительно надеюсь, что это имеет смысл, любая идея или совет будут высоко оценены.
Исходные файлы: http://cl.ly/3V46120C2d3B0A1o3o25 (протестировано на Mac / Chrome стабильный, требуется WebGL)