Creating immersive visualizations using Three.js and WebGL, as well as leveraging 2D canvas for rendering graphics, involves passing the getImageData

Recently diving into WebGL (and 3D graphics in general) using three.js, I'm looking to create multiple textures from a 2D canvas for rendering various meshes, each with its own unique texture.

Simply passing the canvas to a new THREE.Texture() causes all objects to have the same texture, as it updates to match the current canvas. To work around this issue, I attempted storing each canvas array and creating a new texture using new THREE.DataTexture(). However, encountered errors on Chrome and an upside-down texture display on 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);

Encountering error messages such as 'WebGL: INVALID_OPERATION: texImage2D: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array' on Chrome and Safari, while Firefox had the texture displayed upside down.

Referencing Mozilla documentation mentioning the data being a Uint8ClampedArray, attempted resolving the error by creating a new Uint8Array() and passing the data:

var data = new Uint8Array(canvasData.data);

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

Still facing the issue of the texture displaying upside-down. Any insights on what could be causing this?

Answer №1

Lazy initialization is a feature of Three.js, allowing you to initialize textures by triggering 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;
      
      // forcing three to initialize 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>

The alternative method would be to use

renderer.setTexture(texture, slot)
, although this approach may not be future-proof.

"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;
      
      // initializing the texture with slot parameter
      var slot = 0; 
      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>

To flip textures, utilize texture.flipY = true;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the power of async/await in combination with map or foreach

I am facing a challenge in retrieving multiple product prices from my database. I had initially thought of using the map or forEach methods to iterate through them and add up the prices to a variable as shown below: // Get Total exports.getTotal = (req,re ...

Add objects to a web address that leads nowhere but can still be linked to

It has come to my realization that the vagueness of my question might be obstructing my search efforts online. Here is the scenario: I am aiming to create a URL structure like this: http://app.myurl.com/8hhse92 The identifier "8hhse92" does not correspo ...

Creating a File and Resource Manager for Your Express Application: Step-by-Step Guide

I'm interested in creating a website section where users can upload files, such as game mods, for others to download. I envision this section being able to handle a large volume of files and users. How can I achieve this scalability and what architect ...

Retrieve the concealed method's name within the immediately invoked function expression (IIFE

This Meteor sever code has a private method called printFuncName within an IIFE. However, when this method is invoked from a public method, it results in the following error: TypeError: Cannot read property 'name' of null I am curious to unde ...

Exploring JavaScript Regular Expressions in Internet Explorer 11

I am attempting to eliminate all non-digit characters from a string using JavaScript. While this code works properly in FF and Chrome, it does not work in IE11 and no characters are removed. var prunedText = pastedText.replace(/[^\d\.]/g,""); ...

angularJS transformRequest for a specified API call

Looking for a way to pass multipart/formdata through a $resource in Angular so I can post an image. I discovered a helpful solution on this Stack Overflow thread. However, the solution applies to all requests within my modules, and I only want it to apply ...

Manipulate the way in which AngularJS transforms dates into JSON strings

I am working with an object that contains a JavaScript date, structured like this: var obj = { startTime: new Date() .... } When AngularJS converts the object to JSON (for instance, for transmission via $http), it transforms the date into a string as ...

AngularJS: splitting the parent <div> into multiple sections every nth element

I have an array of strings in Javascript and I am attempting to use AngularJS to create nested <div> elements. var arr = ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu"]; My goal is to group every 3 elements together like so. <div class="pare ...

Experiencing a problem during the installation of npm express for testing purposes

Having trouble installing supertest as I keep receiving errors in the terminal. When I run 'supertest -v', it says command not found. Even after installing, I encounter the following error. Any suggestions would be greatly appreciated. I attempte ...

The process of examining a function that includes the invocation of an additional API function in a NodeJS environment

I'm faced with a situation where I have a nested function inside another function. The second function is responsible for making an API call. However, in order to write a unit test for this scenario, I need to avoid actually making the real API call a ...

What methods can be used to troubleshoot an issue of an AngularJS function not being called

I am facing an issue with a dynamic table I have created. The rows and action buttons are generated dynamically when the user clicks on the Devices accordion. However, there seems to be a problem with the function expandCollapseCurrent(). Whenever the use ...

What is the process for creating a hover linear wipe transition using CSS/JS?

It's worth noting that I can't simply stack one image on top of the other because I'll be dealing with transparent images as well. preview of linear wipe ...

Searching through an array of objects in MongoDB can be accomplished by using the appropriate query

Consider the MongoDB collection 'users' with the following document: { _id: 1, name: { first: 'John', last: 'Backus' }, birth: new Date('Dec 03, 1924'), death: new Date('Mar 1 ...

Is it considered safe to modify variables by using this[varName] = something within a function that includes varName as a parameter?

As I continue working on this function, a question arises regarding the safety of changing variables in this manner. In my Angular service, I utilize utility functions where context represents this from the component calling the function. The code snippet ...

Optimizing the JSON date structure

After receiving a datetime value in JSON with the format: Created "/Date(1335232596000)/" I developed a JavaScript function to display this value on the front end. Here's the code snippet: return new Date(parseInt(date.substr(6))); However, the c ...

Retrieve the content within a div tag

Hey there, I have a div set up as follows: <div>1+1</div> I'm looking to send this '1+1' value over to my javascript code and calculate it to get '2'. Appreciate any help in advance! ...

"Facing rendering issues with handlebars.js when trying to display an

I have been diligently working on trying to display an array from my running express app, but despite the lack of errors, nothing is being rendered. Take a look at my handlebars template: <h3>Registered Users:</h3> <h5>{{users}}</h5& ...

Material Angular table fails to sort columns with object values

Currently, I am in the process of developing a web application using Angular Material. One of the challenges I have encountered is displaying a table with sorting functionality. While sorting works perfectly fine on all columns except one specific column. ...

Is there a way for me to view the output of my TypeScript code in an HTML document?

This is my HTML *all the code has been modified <div class="testCenter"> <h1>{{changed()}}</h1> </div> This is my .ts code I am unsure about the functionality of the changed() function import { Component, OnInit } f ...

Enforcing automatic spell check on dynamically generated content

After some experimentation, I have discovered that the spell check feature in most browsers is only activated when the user moves to the next word after inputting text. Another interesting finding is that it is possible to "query" the spellchecker by simpl ...