Three.js canvas.toDataURL иногда пустая

75
4

Я пытаюсь отобразить сцену THREE.js + некоторые наложенные HTML-элементы, используя html2canvas.js. Он работает чаще всего, но не все время.

В случаях сбоя отображаются HTML-элементы (фон, наложения и т.д.), Но ничего больше. Сценарий THREE.js действует так, как будто он полностью пуст, хотя он явно имеет в нем данные. Я могу сказать, что он обычно терпит неудачу для более крупных моделей, но только на ранней стадии рендеринга. В конечном итоге он работает во всех случаях, но более крупные модели занимают около 30 секунд. Как будто я должен дать буферу некоторое время для стабилизации.

html2canvas обрабатывает холст THREE.js, как и следовало ожидать - он просто использует drawImage для рисования холста THREE.js на новом холсте, который в конечном итоге возвращается библиотекой.

В противном случае я стараюсь, чтобы ничто больше не было занято холстом, как в этой скрипте: http://jsfiddle.net/TheJim01/k6dto5sk/63/ (js-код ниже)

Как вы можете видеть, я делаю совсем немного, чтобы попытаться заблокировать цикл визуализации и выполнить только один рендер, когда хочу захватить сцену. Но даже все эти меры предосторожности, похоже, не помогают.

Есть ли лучший способ получить изображение из холста THREE.js? Я могу потенциально сделать эту часть вручную, а затем поменять холст THREE.js на изображение, которое достаточно долго, чтобы html2canvas выполнил свою задачу, а затем замените холст THREE.js. Я бы предпочел не делать этого таким образом, поэтому я не мутирую DOM, если пользователь делает много снимков (ресурсы изображения, ресурсы изображений везде...).

Во всяком случае, вот код. Любые идеи или предложения приветствуются. Благодарю!

var hostDiv, scene, renderer, camera, root, controls, light, shape, theta, aniLoopId, animating;
function snap() {
animating = false;
cancelAnimationFrame(aniLoopId);
renderer.render(scene, camera);

// html2canvas version:
/*
var element = document.getElementById('scenePlusOverlays');
// the input buttons represent my overlays
html2canvas( element, function(canvas) {
// I'd convert the returned canvas to a PNG
animating = true;
animate();
});
*/

// This is basically what html2canvas does with the THREE.js canvas.
var c = document.getElementById('rendererCanvas');
var toC = document.createElement('canvas');
toC.width = c.width;
toC.height = c.height;
var toCtx = toC.getContext('2d');
toCtx.drawImage(c, 0, 0);
console.log(toC.toDataURL('image/png'));
animating = true;
animate();
}
function addGeometry() {
//var geo = new THREE.BoxGeometry(1, 1, 1);
var geo = new THREE.SphereGeometry(5, 32, 32);
var beo = new THREE.BufferGeometry().fromGeometry(geo);
geo.dispose();
geo = null;
var mat = new THREE.MeshPhongMaterial({color:'red'});
var msh;

var count = 10;
count /= 2;
var i = 20;
var topLayer = new THREE.Object3D();
var zLayer, xLayer, yLayer;
for(var Z = -count; Z < count; Z++){
zLayer = new THREE.Object3D();
for(var X = -count; X < count; X++){
xLayer = new THREE.Object3D();
for(var Y = -count; Y < count; Y++){
yLayer = new THREE.Object3D();
msh = new THREE.Mesh(beo, mat);
yLayer.add(msh);
msh.position.set((X*i)+(i/2), (Y*i)+(i/2), (Z*i)+(i/2));
xLayer.add(yLayer);
}
zLayer.add(xLayer);
}
topLayer.add(zLayer);
}
scene.add(topLayer);
}
var WIDTH = '500';//window.innerWidth,
HEIGHT = '500';//window.innerHeight,
FOV = 35,
NEAR = 0.1,
FAR = 10000;

function init() {
hostDiv = document.getElementById('hostDiv');
document.body.insertBefore(hostDiv, document.body.firstElementChild);

renderer = new THREE.WebGLRenderer({ antialias: true, preserverDrawingBuffer: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.domElement.setAttribute('id', 'rendererCanvas');
hostDiv.appendChild(renderer.domElement);

camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 500;

controls = new THREE.TrackballControls(camera, renderer.domElement);

light = new THREE.PointLight(0xffffff, 1, Infinity);
light.position.copy(camera.position);

scene = new THREE.Scene();
scene.add(camera);
scene.add(light);

animating = true;
animate();
}

function animate() {
if(animating){
light.position.copy(camera.position);
aniLoopId = requestAnimationFrame(animate);
}
renderer.render(scene, camera);
controls.update();
}

EDIT: Вызов readPixels или toDataURL описанным способом - я рассмотрел аналогичный метод захвата буфера, но был отложен на количество требуемого асинхронного кода. Что-то вроде этого:

var global_callback = null;
function snapshot_method() {
global_callback = function(returned_image) {
// do something with the image
}
}
// ...
function render() {
renderer.render();
if(global_callback !== null) {
global_callback(renderer.domElement.toDataURL());
global_callback = null;
}
}

спросил(а) 2014-10-17T22:12:00+04:00 6 лет, 5 месяцев назад
1
Решение
114

(В интересах будущих Googlers): вам необходимо установить preserveDrawingBuffer в true при WebGLRenderer:

renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });

(исходный код имел это, но была опечатка). Это необходимо, потому что по умолчанию WebGL не требует, чтобы браузеры сохраняли буферы глубины/цвета после каждого кадра чертежа, поэтому, если вы хотите использовать readPixels или toDataURL, вам нужно явно указать реализации сохранить буфер чертежа, используя этот флаг,

Тем не менее, существует штраф за выполнение этого; спецификация дает некоторые рекомендации, если это проблема для вас:

Хотя иногда желательно сохранить буфер рисования, это может привести к значительной потере производительности на некоторых платформах. По возможности этот флаг должен оставаться ложным и использовать другие методы. Для получения содержимого буфера рисования можно использовать такие методы, как синхронный доступ буфера рисования (например, вызов readPixels или toDataURL в той же функции, которая отображается в буфере рисования). Если автору необходимо отобразить один и тот же буфер чертежа по серии вызовов, можно использовать объект Framebuffer.

Я не уверен, что любое из этих предложений возможно в текущей версии three.js(r68), но другое альтернативное решение (для вызова readPixels или toDataURL на копии холста) обсуждается по нескольким другим вопросам (почему мой холст пуст после преобразования в изображение? Как сохранить изображение из холста Three.js?).

ответил(а) 2014-10-21T01:17:00+04:00 6 лет, 5 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема