Экспорт HTML-холста в виде последовательности изображений
Я создалочень наивный страница, которая займет сцену Three.js (хотя это может быть любой не WebGL<canvas>
анимация) и экспортирует отдельные изображения (или последовательности изображений) для последующего преобразования в видео.
Я делаю это отчасти как способ изучения Python, но идея супер-быстрого прототипирования чего-то в Three.js и последующего экспорта большого гладкого шелковистого видео с высоким разрешением мне очень привлекательна. В прошлом я использовал программное обеспечение для захвата экрана, чтобы захватывать видео, хотя это всегда казалось немного неуклюжим, и падение FPS в реальном времени просвечивало и в финальном видео.
Процесс, который у меня сейчас есть, выглядит следующим образом:
Javascript:
Создайте сцену WebGL и настройте цикл рендеринга.Установите ширину / высоту холста на желаемые размерыРендеринг сцены с использованиемrequestAnimationFrame
Приостановить цикл рендеринга / обновления сценыВызовtoDataURL()
на элементе canvas и получить строку base64POST-запрос к сценарию Python с передачей строки base64 (и другими вещами, например, целевым каталогом для сохранения и записывается ли одно изображение или последовательность)Python:
Удалите тип содержимого заголовка MIME и декодируйте строку base64.Запишите строку в файл изображенияРаспечатать / вернуть строку, которая означает успешное состояние, если файл был записан, в противном случае распечатать сообщение об ошибкеimport base64, cgi, cgitb, datetime, glob, re, os
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()
Javascript:
Получите ответ от скрипта Python и отобразите возвращенное сообщениеВозобновить / обновить цикл рендеринга(Повторите процесс столько раз, сколько нужно)Это то, что я всегда запускаю локально, чтобы не было риска конфликтующих записей или чего-то в этом роде.
Я сделал несколько быстрых тестов, и, кажется, это работает по большей части, хотя и немного медленно.
Я упускаю что-то совершенно очевидное в том, как это делается? Как это можно улучшить? (Особенно на стороне питона вещей ..)Неэффективно ли делать отдельный вызов ajax для каждого изображения? Одним из преимуществ является то, что я могу просто остановить / закрыть вкладку в любое время, и она сохранит все изображения до этого момента. Будет ли какая-то польза от хранения всех этих строк base64 и отправки их в самом конце?КакrequestAnimationFrame
ограничит цикл обновления до 60 кадров в секунду, возможно ли легко установить более низкую частоту кадров? Допустим, по какой-то стилистической причине я хотел бы обновить все со скоростью 15 кадров в секунду, будет ли единственный вариант использоватьsetTimeout(callback, 1000 / targetFPS)
с пониманием того, что это будетс течением времени?Исходя из вышесказанного, эта анимация имеет переменнуюframe
который увеличивается на1
каждый цикл обновления. Этот var затем используется для управления различными частями анимации (например, вращением куба и передачей в вершинные / фрагментные шейдеры для манипулирования цветами и текстурными UV-координатами).
Если бы я хотел имитировать что-то вроде 15 кадров в секунду, я был бы прав в необходимости увеличенияframe
по(60 / 15)
вместо? Есть ли более элегантный способ легко переключаться между ограниченными частотами кадров?
Я действительно надеюсь, что это имеет смысл, любая идея или совет будут высоко оценены.
Исходные файлы: http://cl.ly/3V46120C2d3B0A1o3o25 (Проверено на Mac / Chrome стабильной, требуется WebGL)