Three.js/WebGL和2D Canvas——将getImageData()数组传递给Three.DataText



我是WebGL(和3D图形)的新手,我使用的是three.js。我想做的是从2D画布上创建多个纹理,并用它们来渲染多个网格,每个网格都有不同的纹理。

如果我只是将画布传递给一个新的THREE.Texture()原型,纹理将改变为画布当前的状态。所以我所有的物体都有相同的纹理。我的解决方案是使用getImageData()存储每个画布数组,并通过将该数据传递给新的THREE.DataTexture()原型来创建新纹理。但是,chrome一直抛出错误,当我在firefox上运行它时,纹理显示颠倒。

userName = $nameInput.val();
ctx2d.fillText( userName, 256, 128); 
var canvasData = ctx2d.getImageData(0,0,512,256);
texture = new THREE.DataTexture(canvasData.data, 512, 256, THREE.RGBAFormat);
texture.needsUpdate = true;
material = new THREE.MeshBasicMaterial({ map: texture });
geometry = new THREE.PlaneGeometry(20,10);
textMesh = new THREE.Mesh( geometry, material);
scene.add(textMesh);

Chrome和Safari日志以下错误:WebGL: INVALID_OPERATION: texImage2D:类型UNSIGNED_BYTE但ArrayBufferView不是Uint8Array。无论火狐如何播放它,尽管纹理是颠倒的。

根据Mozilla文档,数据是Uint8ClampedArray。所以假设这是问题所在,我可以通过创建一个新的Uint8Array()并将数据传递给它来解决上述错误,如下所示:

var data = new Uint8Array(canvasData.data);
texture = new THREE.DataTexture(data, 512, 256, THREE.RGBAFormat);

但是它仍然显示上下颠倒。这是怎么呢

Three.js惰性初始化,所以理论上你可以通过调用renderer.render来初始化你的纹理

"use strict";
var camera;
var scene;
var renderer;
var group;
init();
animate();
function init() {
  renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")});
  camera = new THREE.PerspectiveCamera(70, 1, 1, 1000);
  camera.position.z = 300;
  scene = new THREE.Scene();
  
  group = new THREE.Object3D();
  scene.add(group);
  var geometry = new THREE.BoxGeometry(50, 50, 50);
  var ctx = document.createElement("canvas").getContext("2d");
  ctx.canvas.width = 128;
  ctx.canvas.height = 128;
  for (var y = 0; y < 3; ++y) {
    for (var x = 0; x < 3; ++x) {
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillStyle = "rgb(" + (x * 127) + ",192," + (y * 127) + ")"  ;
      ctx.fillRect(0, 0, 128, 128);
      ctx.fillStyle = "white";
      ctx.font = "60px sans-serif";
      ctx.fillText(x + "x" + y, 64, 64);
      var texture = new THREE.Texture(ctx.canvas);
      texture.needsUpdate = true;
      texture.flipY = true;
      var material = new THREE.MeshBasicMaterial({
        map: texture,
      });
      var mesh = new THREE.Mesh(geometry, material);
      group.add(mesh);
      mesh.position.x = (x - 1) * 100;
      mesh.position.y = (y - 1) * 100;
      
      // render to force three to init the texture
      renderer.render(scene, camera);
    }
  }
}
function resize() {
  var width = renderer.domElement.clientWidth;
  var height = renderer.domElement.clientHeight;
  if (renderer.domElement.width !== width || renderer.domElement.height !== height) {
    renderer.setSize(width, height, false);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }
}
function animate() {
  resize();
  group.rotation.x += 0.005;
  group.rotation.y += 0.01;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<canvas></canvas>

你也可以调用renderer.setTexture(texture, slot),但这是错误的,因为基于函数的名称,文档和签名,这不能保证将来会正常工作。

"use strict";
var camera;
var scene;
var renderer;
var group;
init();
animate();
function init() {
  renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")});
  camera = new THREE.PerspectiveCamera(70, 1, 1, 1000);
  camera.position.z = 300;
  scene = new THREE.Scene();
  
  group = new THREE.Object3D();
  scene.add(group);
  var geometry = new THREE.BoxGeometry(50, 50, 50);
  var ctx = document.createElement("canvas").getContext("2d");
  ctx.canvas.width = 128;
  ctx.canvas.height = 128;
  for (var y = 0; y < 3; ++y) {
    for (var x = 0; x < 3; ++x) {
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillStyle = "rgb(" + (x * 127) + ",192," + (y * 127) + ")"  ;
      ctx.fillRect(0, 0, 128, 128);
      ctx.fillStyle = "white";
      ctx.font = "60px sans-serif";
      ctx.fillText(x + "x" + y, 64, 64);
      var texture = new THREE.Texture(ctx.canvas);
      texture.needsUpdate = true;
      texture.flipY = true;
      var material = new THREE.MeshBasicMaterial({
        map: texture,
      });
      var mesh = new THREE.Mesh(geometry, material);
      group.add(mesh);
      mesh.position.x = (x - 1) * 100;
      mesh.position.y = (y - 1) * 100;
      
      // make three init the texture
      var slot = 0; // doesn't matter what slot as we're not 
      renderer.setTexture(texture, slot);
    }
  }
}
function resize() {
  var width = renderer.domElement.clientWidth;
  var height = renderer.domElement.clientHeight;
  if (renderer.domElement.width !== width || renderer.domElement.height !== height) {
    renderer.setSize(width, height, false);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }
}
function animate() {
  resize();
  group.rotation.x += 0.005;
  group.rotation.y += 0.01;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<canvas></canvas>

翻转使用texture.flipY = true;

最新更新