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

Posted

技术标签:

【中文标题】Three.js/WebGL 和 2D Canvas -- 将 getImageData() 数组传递给 Three.DataTexture()【英文标题】:Three.js/WebGL and 2D Canvas -- Passing getImageData() array into Three.DataTexture() 【发布时间】:2017-03-01 10:43:56 【问题描述】:

我是 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: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array。然而Firefox播放它,虽然纹理是颠倒的。

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

var data = new Uint8Array(canvasData.data);

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

但它仍然显示颠倒。怎么回事?

【问题讨论】:

另外,如果有人有更好的方法将单独的画布图像存储为纹理而不使用 getImageData,那也很棒。 在将图像数据添加到纹理之前使用gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,true);。我希望应该有一个 threee.js 等价物。 这在three.js 中不起作用,因为three.js 在上传每个纹理之前调用gl.pixelStore(gl.UNPACK_FLIP_Y_WEBGL, texture.flipY),这意味着它将有效地覆盖您的设置。 【参考方案1】:

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;

【讨论】:

做到了!非常感谢。 three.js 懒惰初始化是什么意思?是不是网格在渲染到场景中才真正“创建”? Initializes lazily 表示在三个.js 需要渲染它们之前,不会创建任何 WebGL 资源、WebGLTexture、WebGLBuffer、WebGLProgram 等。

以上是关于Three.js/WebGL 和 2D Canvas -- 将 getImageData() 数组传递给 Three.DataTexture()的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Three.js(WebGL)贴图(纹理映射)和UV映射

解决 three.js / webGL 中的 gl_PointSize 限制

Three.js / WebGL - 透明平面隐藏在它们后面的其他平面

WebGL初探—Three.js全景图实战

戳HTML5+Three.js+WebGL实现立方体

Three.js(WebGL分支)