1

It seems quite common for people to use this 2d canvas texture method for producing text billboards, sprites, overlays in THREE.js scenes as an alternative to making textures from imported image files e.g. Lee Stemkoski's example.

However when I try such method for text strings longer than a few characters the text in the finished graphic object is truncated.

Here is a JSFiddle

Here is the relevant code

function F_Text_Plane_Make_wo_box(text)

{
    var canvas1 = document.createElement('canvas');
    var context1 = canvas1.getContext('2d');
    context1.font = "Bold 40px Arial";
    context1.fillStyle = "rgba(155,0,200,0.95)";
    context1.fillText(text, 0, 50);

    var texture1 = new THREE.Texture(canvas1);
    texture1.needsUpdate = true;

    var material1 = new THREE.MeshBasicMaterial
    ( { map: texture1, side:THREE.DoubleSide } );
    material1.transparent = true;

    var mesh1 = new THREE.Mesh(
        new THREE.PlaneGeometry(canvas1.width, canvas1.height),
        material1
    );
    return mesh1;
}

It occurs in Chrome, Opera and Firefox on Windows7 laptop.

UPDATE

Thanks to West Langley for clarifying matters. It appears that we can't easily and automatically construct a plane which contains the supplied text string and no trailing white space. This limitation can be worked around using transparency or a fixed-width destination container.

Here is a JSFiddle

Here is relevant code

//=============================================================================
function F_Text_onto_Plane(text, Tbox_width, Tbox_depth)

{

    var canvas1 = document.createElement('canvas');
    var context1 = canvas1.getContext('2d');

    canvas1.width = Tbox_width; canvas1.height = Tbox_depth;

    context1.font = "Bold 40px Arial"; //... font as big and simple as possible so graphics look smooth

    var metric = context1.measureText(text);
    var text_len_pixels = metric.width;

    //------------ Backing Rectangle ------------
    context1.fillStyle = "rgba(255,255,255,0.95)"; //... White
    //..x1,y1, x2,y2 origin in top left corner x increasing rightwards, y increasing downwards.  
    context1.fillRect(0,0, Tbox_width, Tbox_depth);  //... rectangle matches the Tbox_width and Tbox_depth

    context1.fillStyle = "rgba(255,255,0,0.95)"; //... Yellow, probably change to white in practice.
    context1.fillRect(0,0,5+text_len_pixels+5,Tbox_depth);  //... very good fit to the particular text

    //------------- Paint the Text onto canvas ----------------
    context1.fillStyle = "rgba(0,0,0,1)";//... Black.
    context1.fillText(text , 0+5, Tbox_depth-5);//... text string, start_x, start_y (start point is bottom left of first character).

    //------------------------------------------------
    //... canvas contents will be used for a texture
    //... note this takes the everything within the canvas area, including the canvas background.
    var texture1 = new THREE.Texture(canvas1);
    texture1.needsUpdate = true;

    var material1 = new THREE.MeshLambertMaterial( { map: texture1, side:THREE.DoubleSide } );
    material1.transparent = true;

    var mesh1 = new THREE.Mesh(
        new THREE.PlaneGeometry(Tbox_width, Tbox_depth),
        material1 );

    //... can scale the plane e.g.  mesh1.scale.set(100,50,1.0);
    //... alert ("Checkout: http://stackoverflow.com/questions/4532166/how-to-capture-a-section-of-a-canvas-to-a-bitmap?lq=1");
    //... can use .lookat to make plane face a single camera in the scene.
    //... can use THREE.js Sprite to make plane look at all cameras in the scene.

    return mesh1;


}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
steveOw
  • 879
  • 12
  • 41

1 Answers1

2

You need to set the canvas width:

var canvas1 = document.createElement( 'canvas' );
canvas1.width = 512;
canvas1.height = 64;

var texture1 = new THREE.Texture( canvas1 );
texture1.needsUpdate = true;

var material1 = new THREE.MeshLambertMaterial( {  map: texture1, side:THREE.DoubleSide, transparent = true } );

Now, when you create your mesh geometry, you specify its size in world units, not in pixels:

var mesh1 = new THREE.Mesh( new THREE.PlaneGeometry( 1024, 128 ), material1 );

three.js r.70

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • Thanks, that clarifies a lot. Is there a way of computing PlaneGeometry dimensions for mesh1 (instead of hard-wiring) so that texts (of different lengths) are framed neatly with no trailing space? – steveOw Feb 21 '15 at 22:39
  • The `PlaneGeometry` dimensions have no impact on the number of trailing spaces. – WestLangley Feb 22 '15 at 01:43
  • OK, think I get it now. This method does NOT output a mesh whose width is set according to the length of the particular input text string. Guess I can live with that :) – steveOw Feb 22 '15 at 02:35
  • Shouldn't the texture dimensions (and thus the canvas1 dimensions) strictly be set to powers of two e.g. 512 x 64? – steveOw Feb 23 '15 at 00:45
  • Oh yes, that would be highly advisable. The plane geometry can be any size, but should have the same aspect ratio to prevent distortion. – WestLangley Feb 23 '15 at 01:50