blender建模并用babylonjs更换模型上的文字
更新时间: 2025-08-19 14:09:15
效果如下,我自己建了个广告牌模型,平时的时候显示限速信息
前方有事故的时候显示警告
# 建模
我使用blender建模(实际上我也只会这个)
底座是一个立方体,然后我缩放了立法体的上平面,形成一个锥形
柱子就是基础的10边的圆柱体
显示屏是一个长方体,然后内插了一个平面,再向内挤出
然后分离中间屏幕的平面,给它一个基础的黑色材质, 然后注意要将这个屏幕的模型命名为"info",后面写代码要用上的
# 实现步骤
- 首先需要获取到这个平面,并且给它一个自己创建的材质,后面我们就对这个材质的贴图使用canvas生成进行修改即可
import { StandardMaterial } from "@babylonjs/core";
let infoMaterial
// 加载模型
SceneLoader.ImportMesh(
null,
"/models/",
"info.glb",
scene,
(instanceMeshes) => {
// 创建材质
infoMaterial = new StandardMaterial(`infoMaterial`, scene);
// 获取平面
const modelIds = ["info"];
// 赋予材质
modelIds.forEach(id => {
scene.getMeshById(id).material = infoMaterial
})
// 改变材质
showText()
}
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 改变材质主要是通过canvas生成相应文字的纹理,然后将这个纹理赋给infoMaterial的diffuseTexture和emissiveTexture(因为显示屏发光,所以这个需要)
function showText(text='限速70km/h') {
// 生成texture,后面再细说
const texture = createMultiLineTextTexture(text)
infoMaterial.diffuseTexture = texture
infoMaterial.emissiveTexture = texture
}
1
2
3
4
5
6
2
3
4
5
6
- 生成带文字的纹理,需要注意的是,如果文字是“限速70km/h”时文字横排3行,如果是其他的文字,就竖排两行,同时还要注意文字的大小会随文字的字数改变
function createMultiLineTextTexture(text='限速70km/h', color='#ff0000', width = 224, height = 392) {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");
// 清空画布
context.clearRect(0, 0, width, height);
// 1. 检测是否为"限速xxkm/h"格式
const speedLimitPattern = /^限速(\d+)km\/h$/;
const isSpeedLimit = speedLimitPattern.test(text);
if (isSpeedLimit) {
// 2. 特殊格式:横排3行
const match = text.match(speedLimitPattern);
const lines = ["限速", match[1], "km/h"];
drawHorizontalText(context, lines, color, width, height);
} else {
// 普通文字:竖排2列
const lines = splitTextToVerticalLines(text); // 切分文字为两列
const fontSize = calculateVerticalFontSize(context, lines, width, height); // 计算文字大小
// 设置字体样式
context.font = `${fontSize}px Arial Bold`;
context.fillStyle = color;
context.textAlign = "center";
context.textBaseline = "middle";
// 绘制竖排文字
drawVerticalText(context, lines, color, width, height, fontSize);
}
// 创建Babylon纹理
const texture = new Texture("", scene);
texture.updateURL(canvas.toDataURL());
return texture;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- 实现横排文字drawHorizontalText
/**
* 横排3行文字绘制(居中对齐)
* @param {CanvasRenderingContext2D} context - Canvas上下文
* @param {string[]} lines - 3行文字数组
* @param {string} color - 文字颜色
* @param {number} width - Canvas宽度
* @param {number} height - Canvas高度
*/
function drawHorizontalText(context, lines, color, width, height) {
// 计算最佳字体大小(确保3行文字垂直居中且不溢出)
const lineCount = 3;
const maxFontSize = Math.floor(height / (lineCount * 1.5)); // 行高为字体1.5倍
let fontSize = maxFontSize;
// 设置字体样式
context.font = `${fontSize}px Arial Bold`;
context.fillStyle = color;
context.textAlign = "center";
context.textBaseline = "middle";
// 计算行高和总高度
const lineHeight = fontSize * 1.5;
const totalTextHeight = lineCount * lineHeight;
const verticalOffset = (height - totalTextHeight) / 2;
// 绘制每行文字
lines.forEach((line, index) => {
const y = verticalOffset + index * lineHeight + lineHeight / 2;
context.fillText(line, width / 2, y);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- 实现 splitTextToVerticalLines
/**
* 将文字拆分为竖排2行
* @param {string} text - 输入文字
* @returns {string[]} 拆分后的2行文字
*/
function splitTextToVerticalLines(text) {
// 情况1:用户已使用|分隔符指定竖排拆分
if (text.includes("|")) {
return text.split("|").slice(0, 2); // 最多2列
}
// 情况2:自动均衡拆分文字
const chars = text.split("");
const mid = Math.ceil(chars.length / 2);
// 确保左右列数量尽量均衡
return [
chars.slice(0, mid).join(""),
chars.slice(mid).join("")
];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 计算文字大小实现
/**
* 计算竖排文字最佳字体大小
* @param {CanvasRenderingContext2D} context - Canvas上下文
* @param {string[]} lines - 两行文字
* @param {number} width - Canvas宽度
* @param {number} height - Canvas高度
* @returns {number} 最佳字体大小
*/
function calculateVerticalFontSize(context, lines, width, height) {
// 基于文字数量和高度计算初始大小
const maxChars = Math.max(lines[0].length, lines[1].length);
let fontSize = Math.floor(height / (maxChars * 1.5)); // 行高为字体1.5倍
// 检查宽度是否溢出(单字宽度不应超过列宽的80%)
context.font = `${fontSize}px Arial Bold`;
const charWidth = context.measureText("测").width; // 取一个中文字符宽度
const maxColumnWidth = (width / 2) * 0.8; // 每列最大可用宽度
// 调整字体大小以适应宽度
while (charWidth > maxColumnWidth && fontSize > 12) {
fontSize--;
context.font = `${fontSize}px Arial Bold`;
}
return fontSize;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 实现绘制竖排文字 drawVerticalText
/**
* 竖排2行文字绘制(居中对齐)
* @param {CanvasRenderingContext2D} context - Canvas上下文
* @param {string[]} lines - 2行文字数组
* @param {string} color - 文字颜色
* @param {number} width - Canvas宽度
* @param {number} height - Canvas高度
*/
function drawVerticalText(context, lines, color, width, height, fontSize) {
// 计算左右两列位置(竖排分两列)
const columnWidth = width / 2;
// 绘制左侧列文字(垂直排列)
drawVerticalColumn(context, lines[0], columnWidth * 0.5, height, fontSize);
// 绘制右侧列文字(垂直排列)
drawVerticalColumn(context, lines[1], columnWidth * 1.5, height, fontSize);
}
/**
* 垂直绘制一列文字(逐字垂直排列)
* @param {CanvasRenderingContext2D} context - Canvas上下文
* @param {string} text - 文字内容
* @param {number} x - 列X坐标
* @param {number} height - Canvas高度
* @param {number} fontSize - 字体大小
*/
function drawVerticalColumn(context, text, x, height, fontSize) {
const lineHeight = fontSize * 1.5;
const totalHeight = text.length * lineHeight;
const verticalOffset = (height - totalHeight) / 2;
// 逐字垂直排列
for (let i = 0; i < text.length; i++) {
const y = verticalOffset + i * lineHeight + lineHeight / 2;
context.fillText(text[i], x, y);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39