readRenderTargetPixels结果为全零

时间:2017-07-26 15:17:43

标签: three.js

问题:

我试图从渲染目标中读取像素值,但这些值都以零的形式返回。我做错了什么,或者我使用的版本是否已破坏此功能?

我有什么:

一个重要的注意事项是我使用r76卡住了。我知道,我知道,这也让我感到不安。

以下代码实际上就是我所拥有的一切。只需点击" GO"右上角的按钮,它会产生一个警告,说明整个渲染目标缓冲区是否为零。

我尝试过的事情:

我尝试使用最新的three.js(r86),完全相同的代码正常工作。

我还尝试通过将我的render函数安排到渲染到画布(工作正常)或渲染目标(仍为全零)来解决任何异步问题,基于我在外面设置的标志渲染循环。

function render() {
    light.position.copy(camera.position);
    if(doRenderTarget){
        toRenderTarget();
        doRenderTarget = false;
    }
    else{
        renderer.clear();
        renderer.render(scene, camera);
    }
    controls.update();
}

代码:

使用r76:



var hostDiv, scene, renderer, camera, controls, light;

var WIDTH = window.innerWidth,
  HEIGHT = window.innerHeight,
  FOV = 35,
  NEAR = 1,
  FAR = 1000;

var target = new THREE.WebGLRenderTarget();

function toRenderTarget() {
  var size = renderer.getSize();
  var w = WIDTH;
  var h = HEIGHT;
  target.setSize(w, h);
  renderer.clearTarget(target, true, true, true);
  renderer.render(scene, camera, target);
  var renderTargetPixelBuffer = new Uint8Array(w * h * 4);
  renderer.readRenderTargetPixels(target, 0, 0, w, h, renderTargetPixelBuffer);

  var allZeroes = renderTargetPixelBuffer.reduce(function(prev, item) {
    return prev && (item === 0.0);
  }, true);
  if (allZeroes) {
    alert("All zeroes!");
  } else {
    alert("Never mind, the data is okay.");
  }
}
document.getElementById("go").addEventListener("click", toRenderTarget);

function init() {
  hostDiv = document.createElement('div');
  hostDiv.setAttribute('id', 'host');
  document.body.appendChild(hostDiv);

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.setSize(WIDTH, HEIGHT);
  hostDiv.appendChild(renderer.domElement);

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

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  //controls.rotateSpeed = 5.0;
  controls.dynamicDampingFactor = 0.5;
  controls.handleResize();

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

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

  var cube = new THREE.Mesh(
    new THREE.BoxBufferGeometry(50, 50, 50),
    new THREE.MeshLambertMaterial({
      color: "red"
    })
  );
  scene.add(cube);

  animate();
}

function render() {
  light.position.copy(camera.position);
  renderer.render(scene, camera);
  controls.update();
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

init();

window.onresize = function() {
  WIDTH = window.innerWidth;
  HEIGHT = window.innerHeight;
  if (renderer && camera && controls) {
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
    controls.handleResize();
  }
}

body {
  position: relative;
}

<!-- r76 -->
<script src="https://cdn.rawgit.com/mrdoob/three.js/6400f2c9b6ee58e01c005a66f00c7cd1113752aa/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/6400f2c9b6ee58e01c005a66f00c7cd1113752aa/examples/js/controls/TrackballControls.js"></script>

<input type="button" id="go" value="GO" style="position: absolute; top: 0; right: 0" ; />
&#13;
&#13;
&#13;

使用r86:

&#13;
&#13;
var hostDiv, scene, renderer, camera, controls, light;

var WIDTH = window.innerWidth,
  HEIGHT = window.innerHeight,
  FOV = 35,
  NEAR = 1,
  FAR = 1000;

var target = new THREE.WebGLRenderTarget();

function toRenderTarget() {
  var size = renderer.getSize();
  var w = WIDTH;
  var h = HEIGHT;
  target.setSize(w, h);
  renderer.clearTarget(target, true, true, true);
  renderer.render(scene, camera, target);
  var renderTargetPixelBuffer = new Uint8Array(w * h * 4);
  renderer.readRenderTargetPixels(target, 0, 0, w, h, renderTargetPixelBuffer);

  var allZeroes = renderTargetPixelBuffer.reduce(function(prev, item) {
    return prev && (item === 0.0);
  }, true);
  if (allZeroes) {
    alert("All zeroes!");
  } else {
    alert("Never mind, the data is okay.");
  }
}
document.getElementById("go").addEventListener("click", toRenderTarget);

function init() {
  hostDiv = document.createElement('div');
  hostDiv.setAttribute('id', 'host');
  document.body.appendChild(hostDiv);

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.setSize(WIDTH, HEIGHT);
  hostDiv.appendChild(renderer.domElement);

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

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  //controls.rotateSpeed = 5.0;
  controls.dynamicDampingFactor = 0.5;
  controls.handleResize();

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

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

  var cube = new THREE.Mesh(
    new THREE.BoxBufferGeometry(50, 50, 50),
    new THREE.MeshLambertMaterial({
      color: "red"
    })
  );
  scene.add(cube);

  animate();
}

function render() {
  light.position.copy(camera.position);
  renderer.render(scene, camera);
  controls.update();
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

init();

window.onresize = function() {
  WIDTH = window.innerWidth;
  HEIGHT = window.innerHeight;
  if (renderer && camera && controls) {
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
    controls.handleResize();
  }
}
&#13;
body {
  position: relative;
}
&#13;
<!-- r86 -->
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>

<input type="button" id="go" value="GO" style="position: absolute; top: 0; right: 0" ; />
&#13;
&#13;
&#13;

使用r76并将渲染延迟到下一个动画循环:

&#13;
&#13;
var hostDiv, scene, renderer, camera, controls, light;

var WIDTH = window.innerWidth,
  HEIGHT = window.innerHeight,
  FOV = 35,
  NEAR = 1,
  FAR = 1000;

var target = new THREE.WebGLRenderTarget();

var doRenderTarget = false;
function go(){
  doRenderTarget = true;
}

function toRenderTarget() {
  var size = renderer.getSize();
  var w = WIDTH;
  var h = HEIGHT;
  target.setSize(w, h);
  renderer.clearTarget(target, true, true, true);
  renderer.render(scene, camera, target);
  var renderTargetPixelBuffer = new Uint8Array(w * h * 4);
  renderer.readRenderTargetPixels(target, 0, 0, w, h, renderTargetPixelBuffer);

  var allZeroes = renderTargetPixelBuffer.reduce(function(prev, item) {
    return prev && (item === 0.0);
  }, true);
  if (allZeroes) {
    alert("All zeroes!");
  } else {
    alert("Never mind, the data is okay.");
  }
}
document.getElementById("go").addEventListener("click", toRenderTarget);

function init() {
  hostDiv = document.createElement('div');
  hostDiv.setAttribute('id', 'host');
  document.body.appendChild(hostDiv);

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.setSize(WIDTH, HEIGHT);
  hostDiv.appendChild(renderer.domElement);

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

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  //controls.rotateSpeed = 5.0;
  controls.dynamicDampingFactor = 0.5;
  controls.handleResize();

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

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

  var cube = new THREE.Mesh(
    new THREE.BoxBufferGeometry(50, 50, 50),
    new THREE.MeshLambertMaterial({
      color: "red"
    })
  );
  scene.add(cube);

  animate();
}

function render() {
  light.position.copy(camera.position);
  if(doRenderTarget){
    toRenderTarget();
  }
  else{
    renderer.render(scene, camera);
  }
  controls.update();
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

init();

window.onresize = function() {
  WIDTH = window.innerWidth;
  HEIGHT = window.innerHeight;
  if (renderer && camera && controls) {
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
    controls.handleResize();
  }
}
&#13;
body {
  position: relative;
}
&#13;
<!-- r76 -->
<script src="https://cdn.rawgit.com/mrdoob/three.js/6400f2c9b6ee58e01c005a66f00c7cd1113752aa/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/6400f2c9b6ee58e01c005a66f00c7cd1113752aa/examples/js/controls/TrackballControls.js"></script>

<input type="button" id="go" value="GO" style="position: absolute; top: 0; right: 0" ; />
&#13;
&#13;
&#13;

随访:

这里的关键似乎直接从GL上下文中读取,但问题的根本原因要简单得多。我想知道readRenderTargetPixels如何读取它有什么问题,因为它实际上调用了相同的GL方法。它一直在我面前。阻止_gl.readPixels来电:

R76:

if ( ( x > 0 && x <= ( renderTarget.width - width ) ) && ( y > 0 && y <= ( renderTarget.height - height ) ) ) {

R86:

if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {

r76中的函数不允许xy0,我将两者都设置为零。 r86函数允许两者都为0,这就是它工作的原因。

由于r76超级老,我不打算提交错误报告或修复。感谢@evil professeur帮助突出问题!

1 个答案:

答案 0 :(得分:0)

问题是,当您调用该函数时,缓冲区将被清除。你要做的就是在渲染后立即调用它。最简单的方法是在click上设置一个标志,通知渲染函数你想要检索像素,并在渲染后立即调用toRenderTarget。变化看起来像那样:

var getRenderedPixels = false;
function setAwaitFlag() { 
  getRenderedPixels = true;
}

document.getElementById("go").addEventListener("click", setAwaitFlag);

然后在渲染功能

function render() {
  light.position.copy(camera.position);
  renderer.render(scene, camera);
  if (getRenderedPixels) {
    toRenderTarget();
    getRenderedPixels = false;
  }
  controls.update();
}

除此之外,我使用gl.ReadPixels而不是readRenderTargetPixels,然后单击它。这是改变:

var gl = renderer.getContext(),
pixels = new Uint8Array(w * h * 4);
gl.readPixels(0,0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, renderTargetPixelBuffer);

这里是小提琴https://jsfiddle.net/c06b5p8u/1/

但是,您应该缩小读取的像素数以提高性能。 我也被困了一会儿。