Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreeJS: Text Sprite's font size difference between WebGL renderer and Canvas renderer

I am using Three JS to develop a 3d graph. I want to show units of a graph as THREE.SPRITE. For creating SPRITE, I have first created a canvas element and added text to it. Then I have created THREE.Texture with the canvas element that was previously created. Created THREE.SpriteMaterial with the texture as a map and then created THREE.SPRITE using this sprite material. Added this sprite material to scene. When the renderer is an instance of THREE.WebGLRenderer the size of text is very small and when the renderer is an instance of THREE.CanvasRenderer then the size of text is very large.

Following is the code that I have used to create 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.Sprite(material);
var textObject = new THREE.Object3D();
var sprite = new THREE.Sprite(material);
textObject.textHeight = actualFontSize;
textObject.textWidth = (textWidth / textHeight) * textObject.textHeight;
//sprite.scale.set(textObject.textWidth / textWidth, textObject.textHeight / textHeight, 1);
textObject.add(sprite);

scene.add(textObject);

Is it the default behavior or am I doing anything wrong. I am seeking a fix that works in both Canvas and WebGL renderers consistently.

like image 952
Kishor Avatar asked Oct 27 '13 12:10

Kishor


Video Answer


1 Answers

After trying so many combinations, the following code 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);

Add THREE JS (release 62) link and use the following script.

Hope this helps others with similar problems.

Update: Here is the jsfiddle of the above code.

like image 81
Kishor Avatar answered Oct 16 '22 11:10

Kishor