Differences in font size of Text Sprites between ThreeJS's WebGL renderer and Canvas renderer

I am currently working with Three JS to develop a 3D graph and I am facing an issue with displaying units of the graph as THREE.SPRITE. To create a SPRITE, I first generate a canvas element and add text to it. Then I proceed to create a THREE.Texture using the previously created canvas element. After that, I create a THREE.SpriteMaterial with the texture as a map and finally create a THREE.SPRITE using this sprite material, adding it to the scene. However, when using a THREE.WebGLRenderer, the text appears very small, whereas when using a THREE.CanvasRenderer, the text appears very large.

Below is the code snippet I utilized to create the Sprite.

var canvas = document.createElement('canvas'),
    context = canvas.getContext('2d'),
    metrics = null,
    textHeight = 100,
    textWidth = 0,
    actualFontSize = 20;

context.font = "normal " + textHeight + "px Arial";
metrics = context.measureText("Sample Text");
var textWidth = metrics.width;

canvas.width = textWidth;
canvas.height = textHeight;
context.font = "normal " + textHeight + "px Arial"; 
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "#ff0000";
context.fillText("Sample Text", textWidth / 2, textHeight / 2);

var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;

var material = new THREE.SpriteMaterial({ map: texture, useScreenCoordinates: false, alignment: THREE.SpriteAlignment.center });
material.transparent = true;
var textObject = new THREE.Object3D();
var sprite = new THREE.Sprite(material);
textObject.textHeight = actualFontSize;
textObject.textWidth = (textWidth / textHeight) * textObject.textHeight;
textObject.add(sprite);

scene.add(textObject);

Can you please advise if this behavior is default or if there is an issue in my implementation? I am seeking a solution that will ensure consistent text size in both Canvas and WebGL renderers.

Answer №1

Finally, after numerous attempts, I cracked the code that actually worked!

var SCREEN_WIDTH = 400,
    SCREEN_HEIGHT = 300,
    VIEW_ANGLE = 45,
    ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
    NEAR = 0.1,
    FAR = 20000,
    webGLScene = new THREE.Scene(),
    canvasScene = new THREE.Scene(),
    webGLCamera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR),
    canvasCamera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR),
    webGLRenderer = new THREE.WebGLRenderer({ antialias: true }),
    canvasRenderer = new THREE.CanvasRenderer();

webGLScene.add(webGLCamera);
canvasScene.add(canvasCamera);

webGLCamera.position.set(0, 0, 20);
webGLCamera.lookAt(webGLScene.position);

canvasCamera.position.set(0, 0, 20);
canvasCamera.lookAt(canvasScene.position);

webGLRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
canvasRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

container = document.body;
container.appendChild(webGLRenderer.domElement);
container.appendChild(canvasRenderer.domElement);

makeSprite(webGLScene, "webgl");
makeSprite(canvasScene, "2d");

function makeSprite(scene, rendererType) {
    var canvas = document.createElement('canvas'),
        context = canvas.getContext('2d'),
        metrics = null,
        textHeight = 100,
        textWidth = 0,
        actualFontSize = 2;

    context.font = "normal " + textHeight + "px Arial";
    metrics = context.measureText("Sample Text");
    var textWidth = metrics.width;

    canvas.width = textWidth;
    canvas.height = textHeight;
    context.font = "normal " + textHeight + "px Arial";
    context.textAlign = "center";
    context.textBaseline = "middle";
    context.fillStyle = "#ff0000";
    context.fillText("Sample Text", textWidth / 2, textHeight / 2);

    var texture = new THREE.Texture(canvas);
    texture.needsUpdate = true;

    var material = new THREE.SpriteMaterial({ map: texture, useScreenCoordinates: false, alignment: THREE.SpriteAlignment.center });
    material.transparent = true;
    //var textObject = new THREE.Sprite(material);
    var textObject = new THREE.Object3D();
    var sprite = new THREE.Sprite(material);
    textObject.textHeight = actualFontSize;
    textObject.textWidth = (textWidth / textHeight) * textObject.textHeight;
    if (rendererType == "2d") {
        sprite.scale.set(textObject.textWidth / textWidth, textObject.textHeight / textHeight, 1);
    } else {
        sprite.scale.set(textWidth / textHeight * actualFontSize, actualFontSize, 1);
    }

    textObject.add(sprite);

    scene.add(textObject);
}

canvasRenderer.render(canvasScene, canvasCamera);
webGLRenderer.render(webGLScene, webGLCamera);

Include the THREE JS (release 62) link and implement the provided script for a successful outcome.

Hopefully, this solution will benefit those facing similar challenges.

Update: Check out the jsfiddle link for a demonstration of the above code.

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

Retrieving Information from API using Vue.js

In the code snippet below, I am displaying data from an API for all flats on a single page. However, I am facing difficulty in showing the floor number for each flat. The JSON body is as follows: { "response": [ { "fl ...

Tips on creating and elegantly showcasing an image from a PDF document on the screen with React-PDF

I am currently working on developing a user interface in JavaScript (React) to enable users to create PDFs directly on the screen. The concept involves having input boxes on one side, which instantly update the fields on the PDF displayed on the other side ...

Is there a way to ensure that the animation does not stop until it reaches the final frame when the pointer leaves?

My challenge lies in ensuring that an animation triggered by hovering over an element continues to play until it reaches its final frame, even after the mouse pointer leaves the element. How can I make this happen? <mesh name="rond-ui-item-01 ...

What is the best way to sequentially invoke an asynchronous function within an Observable method?

Presently, I have the following method: public classMethod( payload: Payload, ): Observable<Result> { const { targetProp } = payload; let target; return this.secondClass.secondClassMethod({ targetProp }).pipe( delayWhen(() ...

Executing a single Function within the UseEffect Hook

Can anyone assist me with solving this code puzzle? I have a carousel element that includes icons for moving to the previous and next slides. Whenever these icons are clicked, a specific function needs to be triggered within the useEffect() hook. The spec ...

Is there a Ruby gem similar to Readability that anyone can recommend?

Readability is a nifty JavaScript tool that magically transforms a cluttered HTML page into a more user-friendly format. I'm on the hunt for a Ruby implementation or something along those lines - does anyone know of a library that offers similar funct ...

Is it possible to create a collapse and expand animation that does not involve transitioning the `height

After extensively researching, I have come across numerous articles advocating for the use of transform and opacity for achieving smooth CSS transitions. An example can be found here: The prevailing notion always revolves around: ...the optimization ...

What is the best way to display or hide specific tables depending on the button chosen?

Being fairly new to JavaScript, I find myself unsure of my actions in this realm. I've successfully implemented functionality for three links that toggle visibility between different tables. However, my ideal scenario would involve clicking one link t ...

JQuery email validation failing to function

I am new to JQuery and have created a basic validation script to verify email addresses. However, it does not seem to be working properly. Can anyone provide guidance on how to correct this issue? <script> $( "#email" ).blur(function() { var ...

What are some ways to customize the appearance of React Semantic components?

Is there a way to apply CSS for react semantic UI when using create react app? I have installed semantic-ui-react and included the CSS CDN: loginForm.js: import React from "react"; import { Button, Form, Header } from "semantic-ui-react"; import styles f ...

Having trouble getting Three JS to render files in scene 1 format 2?

Currently, I am working on an application that showcases 3D models imported from various modeling software. Specifically, I have an STL file exported from CatiaV5 and a DAE file exported from the latest version of Sketchup. Initially, I was able to succes ...

How to eliminate undefined values from a dropdown selection in AngularJS

Blockquote When choosing a material from the column, the first option is showing as undefined. How can I remove undefined from the drop-down list? What changes need to be made in the HTML/JSON data for this to work properly? Blockquote var app = ang ...

Ways to parse the data from a response received from an Axios POST request

After sending the same POST request using a cURL command, the response I receive is: {"allowed":[],"error":null} However, when I incorporate the POST request in my code and print it using either console.log("response: ", resp ...

Loop through a collection of unique identifiers for documents and establish event listeners in Firestore

Within my Vuex store, there is an action designed to retrieve a list of uids for followed users from the current user's Firestore UserDataCollection document. The action then processes each uid to extract data that will be displayed on the UI. While t ...

The prefixes for Ruby on Rails routes are not properly preprocessed in the .erb.js file

I'm currently working with Rails 4 and encountering an issue with the following file: // apps/assets/javascripts/products.js.erb var getColoursAndMaterialsData = function(onSuccess) { var fd = formdata(); $.post( '<%= data_products_ ...

What is the best way to add animation to my `<div>` elements when my website is first loaded?

I am looking for a way to enhance the appearance of my <div> contents when my website loads. They should gradually appear one after the other as the website loads. Additionally, the background image should load slowly due to being filtered by the wea ...

A guide on iterating through an array to extract the initial character from every string

Currently, my focus is on extracting the initial letter of each word in order to create an acronym. I have set up an array where all the capitalized words are stored, and now I need a way to extract those specific characters. To achieve this, I initially ...

Using JavaScript to dynamically set a background image without the need for manual hard-coding

My attempt was to showcase images as a background image upon mouseover event for the div section with id 'message' by manually coding JavaScript functions for each image like this: Here is the HTML code inside the body section <div id = "mes ...

Sliding off the canvas - concealed navigation

I have implemented CSS to hide a menu on mobile: #filter-column { position:absolute; left:-400px; } However, I want the menu to slide in from the left when the user clicks a link, and everything else should be hidden. When the layer is closed, th ...

Is it necessary to alter the number of rows or columns in the table?

I'm having an issue with my code where the table is not changing the number of rows based on the selected input option. It seems to only read the first value of the select id and does not update the rows accordingly. Can someone help me identify the m ...