Dynamic Textures
A dynamic texture works by creating a canvas onto which you can draw using all the facilities of the HTML5 Canvas.
Creating and Applying
This is simply achieved using the dynamicTexture
function with just three parameters,
const myDynamicTexture = new BABYLON.DynamicTexture(name, option, scene);
though the existing default values of minmaps
, sampling mode
and texture format
can be overwritten by their addition.
Whereas the name and scene parameters are obvious, the option
parameter, which determines the width and height of the dynamic texture, can be one of three values:
- An already created canvas, in which case
canvas.width
andcanvas.height
are used; - An object having the properties
width
andheight
; - A number, in which case both width and height are set to this number.
Once created, the dynamic texture is used as the diffuseTexture
for a material in the usual way:
const myMaterial = new BABYLON.StandardMaterial("Mat", scene);myMaterial.diffuseTexture = myDynamicTexture;mesh.material = myMaterial;
Writing Text
A drawText
method is available so that text can be written directly onto the dynamic texture.
myDynamicTexture.drawText(text, x, y, font, color, canvas color, invertY, update);
Here are the parameters:
- text: string, the words to be written;
- x: number, distance from the left-hand edge;
- y: number, distance from the top or bottom edge, depending on invertY;
- font: string, font definition in the form font-style, font-size, font_name;
- invertY: boolean, true by default in which case y is the distance from the top, when false, y is distance from the bottom and the letters reversed;
- update: boolean, true by default, the dynamic texture will immediately be updated.
Canvas Methods
You can obtain the canvas context using
const ctx = myDynamicTexture.getContext();
exposing all the possibilities of the HTML5 canvas element to give you full control over drawing, transforming, compositing and other pixel manipulation to create or update your dynamic texture in real time.
Drawing Curves
As an example, drawing using a quadratic curve
ctx.beginPath();ctx.moveTo(75, 25);ctx.quadraticCurveTo(25, 25, 25, 62.5);ctx.quadraticCurveTo(25, 100, 50, 100);ctx.quadraticCurveTo(50, 120, 30, 125);ctx.quadraticCurveTo(60, 120, 65, 100);ctx.quadraticCurveTo(125, 100, 125, 62.5);ctx.quadraticCurveTo(125, 25, 75, 25);ctx.stroke();
To apply the drawing, the dynamic texture needs updating
myDynamicTexture.update();
Note: use update(false)
if you do not want to use invertY
.
Images
Images can be added to fill part or the whole of the canvas using the drawImage
method. Remember that you will have to wait for the image to load before assigning it to the canvas and update the dynamic texture afterwards.
For whole image
const img = new Image();img.src = 'PATH TO IMAGE';img.onload = function() {
ctx.drawImage(this, 0, 0);myDynamicTexture.update();
For part of the image scaled onto part of the canvas
const img = new Image();img.src = 'PATH TO IMAGE';img.onload = function() {
ctx.drawImage(this, image start x, image start y, image width, image height, canvas to x, canvas to y, destination width, destination height);myDynamicTexture.update();
Transformations
Anything that is drawn to a canvas can be transformed using methods like scale
, rotate
, or translate
. It is helpful to save
the context before transformaiton and then restore
context after transformation to ensure the texure updates correctly.
const texSize = 512;
ctx.save();
// define position and size of rectangleconst left = texSize * 0.5 - (texSize * 0.125);const top = texSize * 0.5 - (texSize * 0.125);const width = 0.25 * texSize;const height = 0.25 * texSize;
// translate context to center rotation of rectangleconst rotationCenterU = width * 0.5 + left;const rotationCenterV = height * 0.5 + top;ctx.translate(rotationCenterU, rotationCenterV);
// rotate context before drawingctx.rotate(Math.PI/4);
// draw rectanglectx.fillStyle = "Red";ctx.fillRect(-width * 0.5, -height * 0.5, width, height);
ctx.restore();
Playground Combination
This playground combines all the above techniques.
All Dynamic Texture TechniquesText and Area Matching
For a single line of text it is possible to fit the text into a specified area or to fit an area to a specified text.
Fit Text Into an Area.
You have a plane with width and height, planeWidth
and planeHeight
, to form the width and height of the dynamic texture multiply both the plane width and height by the same number to maintain aspect ratio. The number to multiply by defines the sharpness of the text, low numbers produce blurred text. Now you can create the dynamic texture.
const DTWidth = planeWidth * 60;const DTHeight = planeHeight * 60;const dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", { width: DTWidth, height: DTHeight }, scene);
For the next stage using any size of font write and measure the width of text on the dynamic texture.
const ctx = dynamicTexture.getContext();const size = 12; //any value will workctx.font = size + "px " + font_type;const textWidth = ctx.measureText(text).width;
The ratio of text width to size of font applied can be applied to the dynamic texture width to determine the font size that will fit the plane.
const ratio = textWidth / size;
const font_size = Math.floor(DTWidth / ratio);
If you wish you can apply a multiplier to the ratio, the larger the multiplier the smaller the text and bigger margins.
const font_size = Math.floor(DTWidth / (ratio * 1.1));
Now set the font and draw the text
const font = font_size + "px " + font_type;dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
Finally set and apply the material for the plane.
const mat = new BABYLON.StandardMaterial("mat", scene);mat.diffuseTexture = dynamicTexture;plane.material = mat;
Fit an Area to Text
Take a plane mesh of fixed height, planeHeight and text with a set font size you can then calculate how wide the plane must be for the text to fit in. In order to do this you need to know the width the text will take up on a dynamic texture. This is calculated using a temporary dynamic texture and measuring the text.
const temp = new BABYLON.DynamicTexture("DynamicTexture", 64, scene);const tmpctx = temp.getContext();tmpctx.font = font;const DTWidth = tmpctx.measureText(text).width;
Choose a height for the dynamic texture, DTHeight and to maintain the aspect ratio of the plane to the dynamic texture:
- calculate the
ratio
planeHeight:DTHeight; - use the ratio to calculate the width of the
planeWidth
= DTWidth * ratio; - create the dynamic texture to use as material with width and height, DTWidth and DTHeight;
- draw text on dynamic texture;
- create the plane with width and height planeWidth and planeHeight.
const planeHeight = 3;const DTHeight = 256; //or set as wishedconst ratio = planeHeight / DTHeight;const planeWidth = DTWidth * ratio;
const dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", { width: DTWidth, height: DTHeight }, scene, false);const mat = new BABYLON.StandardMaterial("mat", scene);mat.diffuseTexture = dynamicTexture;dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true); //use of null, null centers the text
const plane = BABYLON.MeshBuilder.CreatePlane("plane", { width: planeWidth, height: planeHeight }, scene);plane.material = mat;
Serialization
The dynamic texture can be serialized with the scene using SceneSerializer.Serialize()
or a mesh using SceneSerializer.SerializeMesh()
.
Note: Be sure that the scene is ready before serialization.
This playground demonstrates serializing a dynamic texture associated with a material on a mesh:
Drawing Text and a Curve Texture Serialized Mesh