What is the best method for displaying over 1000 2D text labels using three.js?

Currently, I am in the process of designing a floor plan that includes over 1000 2D meshes with shapeGeometry and basicMeshMaterial on the scene. I also need to incorporate 2D text within these meshes while ensuring that the text is hidden within the bounds of each mesh.

I have experimented with TextGeometry, troika-three-text, CSS2dRenderer, but none of them performed well with such a large number of elements. Can anyone recommend the best approach for rendering a large amount of text efficiently?

To see what I have achieved so far with several elements, you can visit this example. For a demonstration with many elements, you can check out this example. Here is an example of the desired outcome:

Answer №1

To optimize, consider utilizing instancing.

Utilizing InstancedBufferGeometry of PlaneGeometry, along with a texture atlas, can offer significant benefits.

body{
  overflow: hidden;
  margin: 0;
}
<script async src="https://ga.jspm.io/npm:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d3b6a0febebcb7a6bfb6fea0bbbeaba093e2fde5fde0">[email protected]</a>/dist/es-module-shims.js" crossorigin="anonymous"></script>
<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a3d7cbd1c6c6e3938d9296908d93">[email protected]</a>/build/three.module.js",
      "three/addons/": "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0b7f63796e6e4b3b253a3e38253b">[email protected]</a>/examples/jsm/"
    }
  }
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
console.clear();

let scene = new THREE.Scene();
scene.background = new THREE.Color(0xface8d);
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(3, 5, 8).setLength(40);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", (event) => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));

scene.add(new THREE.GridHelper());

let ig = new THREE.InstancedBufferGeometry().copy(new THREE.PlaneGeometry(2, 1));
ig.instanceCount = Infinity;
const amount = 2048;
let instPos = new Float32Array(amount * 3);
for(let i = 0; i < amount;i++){
  instPos[i * 3 + 0] = THREE.MathUtils.randFloatSpread(50);
  instPos[i * 3 + 1] = THREE.MathUtils.randFloatSpread(50);
  instPos[i * 3 + 2] = THREE.MathUtils.randFloatSpread(50);
}
ig.setAttribute("instPos", new THREE.InstancedBufferAttribute(instPos, 3));

let im = new THREE.ShaderMaterial({
  uniforms: {
    quaternion: {value: new THREE.Quaternion()},
    markerTexture: {value: getMarkerTexture(4096, 32, 64)},
    textureDimensions: {value: new THREE.Vector2(32, 64)}
  },
  vertexShader: `
    uniform vec4 quaternion;
    uniform vec2 textureDimensions;
    
    attribute vec3 instPos;
    
    varying vec2 vUv;
    
    vec3 qtransform( vec4 q, vec3 v ){ 
      return v + 2.0*cross(cross(v, q.xyz ) + q.w*v, q.xyz);
    } 
    
    void main(){
      vec3 pos = qtransform(quaternion, position) + instPos;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.);
      
      float iID = float(gl_InstanceID);
      float stepW = 1. / textureDimensions.x;
      float stepH = 1. / textureDimensions.y;
      
      float uvX = mod(iID, textureDimensions.x);
      float uvY = floor(iID / textureDimensions.x);
      
      vUv = (vec2(uvX, uvY) + uv) * vec2(stepW, stepH);
    }
  `,
  fragmentShader: `
    uniform sampler2D markerTexture;
    
    varying vec2 vUv;
    
    void main(){
      vec4 col = texture(markerTexture, vUv);
      gl_FragColor = vec4(col.rgb, 1);
    }
  `
})

let io = new THREE.Mesh(ig, im);
scene.add(io);

let clock = new THREE.Clock();

renderer.setAnimationLoop((_) => {
  let t = clock.getElapsedTime();
  controls.update();
  im.uniforms.quaternion.value.copy(camera.quaternion).invert();
  renderer.render(scene, camera);
});

function getMarkerTexture(size, amountW, amountH){
  let c = document.createElement("canvas");
  c.width = size;
  c.height = size;
  let ctx = c.getContext("2d");
  
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, c.width, c.height);

  const stepW = c.width / 32;
  const stepH = c.height / 64;
  
  ctx.font = "bold 40px Arial";
  ctx.textBaseline = "middle";
  ctx.textAlign = "center";
  ctx.fillStyle = "#000";
  
  let col = new THREE.Color();
  
  let counter = 0;
  for(let y = 0; y < amountH; y++){
    for(let x = 0; x < amountW; x++){
      
      let textX = (x + 0.5) * stepW;
      let textY = ((amountH - y - 1) + 0.5) * stepH;
      ctx.fillText(counter, textX, textY);
      
      ctx.strokeStyle = '#' + col.setHSL(Math.random(), 1, 0.5).getHexString();
      ctx.lineWidth = 3;
      ctx.strokeRect(x * stepW + 4, y * stepH + 4, stepW - 8, stepH - 8);
      
      counter++;
    }
  }
  
  let ct = new THREE.CanvasTexture(c);
  ct.colorSpace = THREE.SRGBColorSpace;
  return ct;
}
</script>

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

Error message "Undefined error encountered when attempting to display an array of objects using jQuery and PHP through AJAX on the console"

While trying to parse a JSON using jQuery and AJAX, I encountered an issue where some objects in the Array, like the SUM(amountbet) object, are showing up as "undefined" in the console. https://i.sstatic.net/pMUqc.png The image above depicts th ...

Tips for triggering a JavaScript function within WordPress using an inline function call

While setting up my plugin in the WordPress admin area, I encountered an issue with a form that stores user information. In my file input type, there is a JavaScript function call to my custom JavaScript that I have linked. Here is the line of code causing ...

Is there a way to load jQuery after the page has loaded? Any suggestions on how

Exploring jQuery $(document).ready(function(){ $('.categories').live('change', function() { var my_location = window.location.pathname.replace('admin/', ''); $(this).parents("tr").next("tr.subcat ...

Monitoring changes in an array of objects using AngularJS's $watch function

Exploring the world of AngularJS, I'm on a quest to solve a challenging issue efficiently. Imagine having an array of objects like this: var list = [ {listprice: 100, salesprice:100, discount:0}, {listprice: 200, salesprice:200, discount:0}, {listpr ...

Utilizing Google App Engine for seamless deployment, integrating Ajax for dynamic interactions, and

Using the google App Engine, I am looking to implement javascript (or Ajax) for POSTing a form and updating the target div. The form includes multiple fields and files for transfer. The javascript function I am using is extracted from the "Javascript: The ...

Transform the JSON object into a TypeScript array

Currently working on a project that requires converting a JSON object into a TypeScript array. The structure of the JSON is as follows: { "uiMessages" : { "ui.downtime.search.title" : "Search Message", "ui.user.editroles.sodviolation.entries" : ...

Function starting too slow due to rapid Loading Spinner Image display

I am struggling with a function that is supposed to display the contents of posts when clicked on. My goal is to have a loading spinner appear for a few seconds before the post content shows up. However, I am facing an issue where the spinner only appears ...

What could be causing the javascript slidetoggle function to keep closing?

I have a link on a page that toggles the search form on and off. Let's say you search for Spring term classes, then you want to search for a new term and click "Start a new Search." The link works great and toggles just fine...BUT the toggle automatic ...

Tips for transforming a magenta.js note sequence into a MIDI file

I need to convert a note sequence using Magenta.js into a midi file and generate a url for users to download. The goal is to use this url in a midi-player/visualizer. // Creating a Magenta note sequence generateMelody(sendedNotes, 0.7, document.getElementB ...

Crop box overlaying video background

Utilizing jCrop to establish a crop region for a video element. My goal is to make the video section within the crop area fill a container with identical proportions, but I'm encountering challenges with the mathematical calculations. The sizes of th ...

I am attempting to set up React, but I keep encountering an issue that says 'Error: EPERM: operation not allowed'

My attempts to set up React have been unsuccessful so far. Initially, I tried installing it using the command npx create-react-app ./, which did not work. Then, I attempted to run npm init react-app my-app, but unfortunately, that also failed. The error me ...

Transferring a JavaScript array using AJAX and PHP

Having an issue with passing the array from my AJAX PHP file, here's how it is set up: [[1,2,3],[1,2,3]] To send it, I use json_encode like this: echo json_encode($array); This is my AJAX script: $.ajax( { url: ...

Ways to eliminate all characters preceding a certain character within an array dataset

I am currently working on parsing a comma-separated string retrieved from a web service in my application, which contains a list of user roles. My goal is to convert this string into an array using jQuery, and I have successfully achieved that. However, I ...

Altering iframe Content with the Help of Selenium Web Driver

I am looking to update the content of an iframe with new material using Selenium WebDriver. Note: I have attempted the following method: driver.swithTo().frame(frame_webelement); driver.findElement(By.xxx).sendKeys("Mycontent"); While I was able to cle ...

The functionality of window.localStorage.removeItem seems to be malfunctioning when used within an $http

Currently, I am sending a post request to my web service and upon successful completion, I am attempting to remove a token from local storage. Here is the code snippet: $http.post("MyService/MyAction").success(function (res) { if ( ...

Ajax Multidimensional Array Response

Currently, I am attempting to retrieve a Multiarray response from an ajax Post JSON request. After numerous attempts, I am hopeful that I can get assistance here... JS AJAX RESPONSE var data = { "myid": "1234" }; $(".the-return").html(""); $.ajax({ ...

"when using .each to parse JSON, the result comes

Hello! I am facing an issue with the code snippet provided below: $.ajax({'type':'POST', 'url':'https://www.123.com/site/gethistory', 'data':{'a': this.username, 'b': username}, ...

Data object constructor is not triggered during JSON parsing

Currently, I am retrieving data from a server and then parsing it into TypeScript classes. To incorporate inheritance in my classes, each class must be capable of reporting its type. Let me explain the process: Starting with the base class import { PageE ...

Creating a program to ascertain whether two integers inputted by a user are odd numbers

I need assistance in creating a function that can ascertain whether two numbers input by the user are odd and then return a boolean value that is true only if both numbers are odd. I'm already familiar with creating a function to check for even number ...

Retrieve all records from the database using a Sequelize query that fall within the timeframe specified by the start

Currently, I'm attempting to retrieve data from a database using Sequelize, filtering for items that were created within a specific date range. Despite implementing the $between operator in my query, I'm facing difficulties as I'm not receiv ...