2014-10-17 26 views
2

我試圖渲染一個THREE.js場景+一些使用html2canvas.js重疊的HTML元素。它工作最多次,但不是所有的時間。Three.js canvas.toDataURL有時爲空

在失敗的情況下,HTML元素呈現(背景,疊加等),但沒有別的。 THREE.js場景就好像它完全是空的,儘管它明顯有數據。我可以說,它通常對較大型號不合格,但只是在渲染之前。它確實可以在所有情況下都可以工作,但較大的型號需要大約30秒。就好像我必須給緩衝區一段時間才能穩定下來。

html2canvas按照您的預期處理THREE.js畫布 - 它只是使用drawImage將THREE.js畫布繪製到最終由庫返回的新畫布上。

否則,我儘量保證其他什麼都忙於在畫布上,就像這個小提琴:http://jsfiddle.net/TheJim01/k6dto5sk/63/(以下js代碼)

正如你所看到的,我做了不少嘗試塊渲染循環,並且當我想捕捉場景時再執行一次渲染。但即使所有這些預防措施似乎都沒有幫助。

有沒有更好的方法從THREE.js畫布中獲取圖像?我可能會手動執行該部分,然後換出THREE.js畫布以獲取足夠長的HTML圖片以完成它的操作,然後將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(); 
} 

編輯: 調用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; 
    } 
} 
+1

我認爲你有一個錯字......'preserverDrawingBuffer'應該是'preserveDrawingBuffer'。這是否解決了這個問題? – caseygrun 2014-10-20 00:04:30

+0

你是對的。我現在一定在我的模板代碼中有這麼多年了......我跑了一些快速壓力測試,看起來錯字是造成這個問題的原因。我很驚訝,甚至在一段時間內工作!如果您將評論添加爲答案,我會接受它。 – TheJim01 2014-10-20 21:11:31

回答

6

(將來的Google的利益):您需要設置preserveDrawingBuffertrue當實例WebGLRenderer

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

(原代碼了這一點,但有一個錯字)。這是必需的,因爲默認情況下,WebGL不需要瀏覽器在每個圖紙框架後保存深度/顏色緩衝區,所以如果要使用readPixelstoDataURL,則需要明確告訴實施方案以使用該標誌保存繪圖緩衝區。

但是,這樣做會造成性能損失; the spec provides some guidance if this is a problem for you

雖然有時需要保留繪製緩衝區,它可以 導致在某些平臺上顯著的性能損失。每當 可能這個標誌應該保持假和其他技術使用。 像同步繪製緩衝區訪問(例如,在渲染到 繪圖緩衝區的相同函數中調用 readPixels或toDataURL)等技術可用於獲取繪圖緩衝區的內容。 如果作者需要通過調用的一系列 呈現到同一個繪圖緩衝區,則可以使用幀緩衝區對象。

我也不清楚這些建議是在three.js所(R68)的當前版本是可行的,但另一種替代解決方案(調用readPixelstoDataURL在畫布的複印件)在幾個討論其他問題(Why does my canvas go blank after converting to image?,How do you save an image from a Three.js canvas?)。

+0

我在原始文章中添加了評論/代碼以繼續對話。 – TheJim01 2014-10-21 14:00:55

+0

實際上,它看起來像'preserveDrawingBuffer'不是必需的:https://stackoverflow.com/a/30647502 – dmnsgn 2017-08-02 10:49:30