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 限制