Three.js / WebGL和2D Canvas-将getImageData()数组传递给Three.DataTexture() [英] Three.js/WebGL and 2D Canvas -- Passing getImageData() array into Three.DataTexture()

查看:82
本文介绍了Three.js / WebGL和2D Canvas-将getImageData()数组传递给Three.DataTexture()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是WebGL(和一般3D图形)的新手,并且正在使用three.js。我想做的是从2D画布上创建多个纹理,然后使用它们渲染多个具有不同纹理的网格。

I'm new to WebGL (and 3D graphics in general) and am using three.js. What I want to do is create multiple textures from a 2D canvas, and use them to render multiple meshes, each with a different texture.

如果我只是将画布传递给一个新的THREE.Texture()原型,纹理将更改为当前的画布。因此,我所有的对象都具有相同的纹理。我的解决方案是使用getImageData()存储每个画布数组,并通过将数据传递到新的THREE.DataTexture()原型来创建新纹理。但是,chrome不断抛出错误,当我在Firefox上运行时,纹理显示为上下颠倒。

If I just pass the canvas to a new THREE.Texture() prototype, the texture will change to whatever the canvas currently is. So all my objects have the same texture. My solution was to store each canvas array using getImageData() and create a new texture by passing that data to a new THREE.DataTexture() prototype. However, chrome keeps throwing errors, and when I run it on firefox, the texture is displayed upside down.

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。尽管纹理是颠倒的,但无论是Firefox还是使用它。

Chrome and Safari log the following error: WebGL: INVALID_OPERATION: texImage2D: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array. However firefox plays it, though the texture is upside down.

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

According to the Mozilla documentation, the data is a Uint8ClampedArray. So assuming this is the problem, I can get around the above error by creating a new Uint8Array() and passing the data to it, per below:

var data = new Uint8Array(canvasData.data);

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

但是它仍然显示颠倒。

However its still displaying upside down. What's going on?

推荐答案

Three.js会延迟进行初始化,因此从理论上讲,您可以通过调用 renderer来初始化纹理.render

Three.js initializes lazily so theoretically you could init your textures by calling 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)可以,但是基于函数的名称,文档和签名可以说是错误的事情,不能保证将来会起作用。

You can also call renderer.setTexture(texture, slot) which works but is arguably the wrong thing to do as based on both the name, docs, and signature of the function there's no guarantee this will work in the future.

"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;

这篇关于Three.js / WebGL和2D Canvas-将getImageData()数组传递给Three.DataTexture()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆