Skip to content

BabylonJS - 动态纹理

Babylon.js - 动态纹理 (DynamicTexture)

Section titled “Babylon.js - 动态纹理 (DynamicTexture)”

DynamicTexture 是 Babylon.js 中一种特殊的纹理类型,它允许你使用标准的 HTML5 2D Canvas API 在其上进行绘制。这对于动态生成纹理非常有用,例如显示文本、绘制形状、渲染 UI 元素或直接在材质上创建程序化图案。

你可以将其想象成纹理背后有一个隐藏的 2D canvas。你可以获取这个 canvas 的绘图上下文,使用熟悉的 API 方法,如 fillText、strokeRect、beginPath、bezierCurveTo、drawImage 等,然后更新纹理以在 3D 模型上显示你的绘制内容。

通过提供名称、定义其大小(宽度和高度)的选项、场景以及可选的是否生成 mipmaps 来创建 DynamicTexture。

import * as BABYLON from '@babylonjs/core';
// 假设 'scene' 已定义
const textureWidth = 512;
const textureHeight = 256;
const generateMipMaps = true; // 是否生成 mipmaps
const myDynamicTexture = new BABYLON.DynamicTexture(
"dynamicTex",
{ width: textureWidth, height: textureHeight }, // 也可以只传递大小: 512
scene,
generateMipMaps
);

DynamicTexture 提供了一个方便的 drawText 方法,用于快速在纹理上渲染文本。

const text = "Hello Babylon!";
const x = 50; // 在纹理 canvas 上的 X 坐标
const y = 100; // 在纹理 canvas 上的 Y 坐标
const font = "bold 44px Arial"; // CSS 字体字符串
const color = "white"; // 文本颜色
const canvasColor = "blue"; // 文本区域的背景颜色 (可以是 'transparent' 透明)
const invertY = true; // 如果为 true,Y 表示距顶部的距离;如果为 false,表示距底部的距离
const update = true; // 如果为 true,立即更新纹理
myDynamicTexture.drawText(text, x, y, font, color, canvasColor, invertY, update);

对于更复杂的绘制,你可以使用 getContext() 方法获取纹理底层 canvas 的 2D 渲染上下文。这使得你可以访问完整的 HTML5 Canvas API。

// 获取 2D 上下文
const ctx = myDynamicTexture.getContext();
// 现在使用标准的 canvas 方法
ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 50);
ctx.strokeStyle = "yellow";
ctx.lineWidth = 4;
ctx.strokeRect(120, 10, 80, 80);
ctx.beginPath();
ctx.moveTo(250, 30);
ctx.lineTo(350, 130);
ctx.lineTo(250, 130);
ctx.closePath();
ctx.fillStyle = "rgba(255, 0, 0, 0.7)"; // 半透明红色
ctx.fill();
ctx.stroke();
// 重要: 使用上下文绘制后,你必须调用 update()
// 才能在 3D 模型上看到更改。
myDynamicTexture.update();

一旦创建并绘制完成,你可以将 DynamicTexture 应用于材质,就像应用任何其他纹理一样(例如,作为 diffuseTexture)。

// 假设 'mesh' 已定义
const material = new BABYLON.StandardMaterial("dynamicMat", scene);
material.diffuseTexture = myDynamicTexture;
mesh.material = material;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Babylon.js - Dynamic Texture Text</title>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<style>
html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; }
#renderCanvas { width: 100%; height: 100%; touch-action: none; }
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script type="text/javascript">
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = function () {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.3, 0.3, 0.3, 1.0);
const camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2.5, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.9;
const box = BABYLON.MeshBuilder.CreateBox("box", { size: 3 }, scene);
// Create Dynamic Texture
const dynamicTexture = new BABYLON.DynamicTexture("dynamic texture", { width: 512, height: 256 }, scene, true);
// Apply texture to material
const material = new BABYLON.StandardMaterial("Mat", scene);
material.diffuseTexture = dynamicTexture;
box.material = material;
// Draw text onto the texture
const font = "bold 70px Arial";
dynamicTexture.drawText("Box Text", 80, 135, font, "green", "white", true, true);
// Draw text requires an update, but the last param 'true' handles it here.
// If drawing with context, call dynamicTexture.update() afterwards.
// 绘制文本需要更新,但最后一个参数 'true' 在这里已经处理了。
// 如果使用上下文绘制,请在之后调用 dynamicTexture.update()。
return scene;
};
const scene = createScene();
engine.runRenderLoop(function () { scene.render(); });
window.addEventListener("resize", function () { engine.resize(); });
</script>
</body>
</html>

这个示例展示了一个在中心旋转的简单立方体。立方体的表面应用了动态生成的纹理:一个白色背景上绘制着加粗绿色 Arial 字体的文本 “Box Text”。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Babylon.js - Dynamic Texture Canvas Drawing</title>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<style>
html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; }
#renderCanvas { width: 100%; height: 100%; touch-action: none; }
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script type="text/javascript">
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = function () {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.1, 0.2, 0.3, 1.0);
const camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 4, 25, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.8;
const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 15, height: 15 }, scene);
// Create Dynamic Texture (square)
const dynamicTexture = new BABYLON.DynamicTexture("dynamic texture", 512, scene, true);
// Apply texture to ground material
const material = new BABYLON.StandardMaterial("Mat", scene);
material.diffuseTexture = dynamicTexture;
ground.material = material;
// Get context and draw using Canvas API
const ctx = dynamicTexture.getContext();
// Clear background (optional)
ctx.fillStyle = "#4488AA"; // 偏蓝色的背景
ctx.fillRect(0, 0, 512, 512);
// Draw a bezier curve (e.g., a heart shape)
// 绘制一条贝塞尔曲线 (例如,心形)
ctx.beginPath();
ctx.moveTo(256, 150); // 起始点略微偏离中心顶部
ctx.bezierCurveTo(256, 130, 230, 100, 190, 100); // 左上方曲线
ctx.bezierCurveTo(120, 100, 120, 180, 120, 180); // 左侧曲线
ctx.bezierCurveTo(120, 250, 190, 310, 256, 350); // 底部点曲线
ctx.bezierCurveTo(322, 310, 392, 250, 392, 180); // 右侧曲线
ctx.bezierCurveTo(392, 180, 392, 100, 332, 100); // 右上方曲线
ctx.bezierCurveTo(290, 100, 256, 130, 256, 150); // 连接回到起始点附近
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
ctx.strokeStyle = "darkred";
ctx.lineWidth = 5;
ctx.stroke();
// Update the texture to show the drawing
// 更新纹理以显示绘制内容
dynamicTexture.update();
return scene;
};
const scene = createScene();
engine.runRenderLoop(function () { scene.render(); });
window.addEventListener("resize", function () { engine.resize(); });
</script>
</body>
</html>

这个示例显示了一个地面平面。应用到地面的纹理是动态生成的。它展示了一个偏蓝色的背景,中心有一个用贝塞尔曲线(通过 canvas 上下文绘制)绘制的大红色心形。

频繁地更新 DynamicTexture,特别是尺寸较大的纹理(例如,每帧更新),会影响性能,因为它涉及 CPU 侧的绘制和 GPU 的纹理上传。对于不经常变化的元素,或者通过优化更新频率来谨慎使用。对于高度动态的文本或 UI,可以考虑使用 Babylon.js GUI 系统,它通常性能更佳。