本章主要学习知识点
- 了解并掌握如何使用CSS2DRenderer渲染器
- 学习如何设置标签位置
- 掌握如何制作标签指示线
- 实现标签交互效果
- 了解并掌握如何使用CSS3DRenderer渲染器
- 掌握如何批量标注标签
- 学会如何使用精灵模型进行标签的渲染
CSS2DRenderer
CSS2DRenderer 是一个特殊的渲染器,用于将HTML元素(如文字标签、按钮)与3D场景中的物体绑定,形成始终面向屏幕的2D交互界面
核心功能与特性
- 2D标签与3D场景融合
可以将HTML元素(如<div>
)作为标签附加到3D模型上,例如地图标注、设备名称等 这就好像在3D模型上贴便利贴,文字始终正对屏幕。 - 自动坐标同步
标签会跟随3D模型的位置移动/旋转,无需手动更新坐标 - 轻量级交互
支持HTML的点击、悬停等事件,适合制作可交互的UI(如信息面板)
适用场景
- 工业模型标注:设备参数实时显示
- 地图POI点:建筑物名称标签
- 数据可视化:3D图表上的数值标签
一个常见的示例
要使用该渲染器需要先引入对应的库
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
创建标签DOM
<div id="tag">这是一个立方体</div>
将DOM元素包装成Three.js可识别的对象
const div = document.getElementById('tag')
// 创建一个CSS2DObject对象,参数为div
const label = new CSS2DObject(div)
const wp = new THREE.Vector3()
// 获取cube对象在世界坐标系中的位置
cube.getWorldPosition(wp)
label.position.set(wp.x, wp.y+0.3, wp.z)
scene.add(label)
初始化渲染器
css2Renderer = new CSS2DRenderer()
css2Renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器尺寸为窗口尺寸
css2Renderer.domElement.style.position = 'absolute' // 设置渲染器元素位置为绝对定位
css2Renderer.domElement.style.top = 0 // 设置渲染器元素顶部距离为0
css2Renderer.domElement.style.pointerEvents = 'none' // 处理鼠标事件遮挡
css2Renderer.render(scene, camera)
document.querySelector('.container').appendChild(css2Renderer.domElement)
最后需要同步一下渲染
renderer.render( scene, camera );
css2Renderer.render(scene, camera)
效果图
标签位置设置
绑定到模型局部坐标
const label = new CSS2DObject(div元素);
model.add(label); // 将标签作为模型的子对象
label.position.set(0, 2, 0); // 相对于模型的位置(Y轴向上偏移2单位)
标签会随父模型移动/旋转自动更新位置
绑定到世界坐标
const worldPos = new THREE.Vector3();
model.getWorldPosition(worldPos); // 获取模型的世界坐标
label.position.copy(worldPos); // 直接设置标签位置
给模型添加标签,并设置各标签位置
loader.load('model/sewing_factory/scene.gltf', function (gltf) {
gltf.scene.traverse((child) => {
if (child.isMesh) {
child.position.set(Math.random() * 50, 0, Math.random() * 30);
const div = document.createElement('div');
div.className = 'tag';
div.innerText = '标签' + child.name;
div.style.color = 'deeppink';
div.style.padding = '4px 8px';
div.style.border = '1px solid deepskyblue';
div.style.background = 'rgba(255,255,255,0.5)';
div.style.borderRadius = '5px';
div.style.position = 'absolute';
document.querySelector('.container').appendChild(div);
const tag = new CSS2DObject(div);
tag.position.set(0, 6.5, 0)
child.add(tag);
}
})
scene.add(gltf.scene);
});
这里将几个模型随机放置在不同的位置,并将标签作为模型的子对象进行添加,我们可以看到,当旋转查看时,标签始终跟随模型
标签指示线
标签指示线,可以理解为在3D场景中为特定模型添加文字标注,并用线条连接标签与目标点(如设备名称+指向箭头)。
我们导入模型,来实现为模型添加标签,并设置指示线指向模型
const loader = new GLTFLoader(loadingManager);
loader.load('model/electrical_box_-_free/scene.gltf', function (gltf) {
const div = document.getElementById('tag');
const tag = new CSS2DObject(div);
div.style.display = 'block';
tag.position.set(0, 1.6, 0);
gltf.scene.add(tag);
scene.add(gltf.scene);
});
标签与指示线结构如下
<div id="tag" style="display: none;">
<img src="/images/common/tip-border.png" alt="">
<div class="content">
电力控制箱
</div>
<div class="line"></div>
</div>
设置线条样式,并控制其位置,将看到如下效果
标签交互
实现一个点击模型展示对应标签的简单交互效果,标签的本质是一个HTML标签,这里还是使用CSS2DRenderer
来实现
导入模型,我们需要创建一个射线,用来检测点击位置是否与模型相交,如果相交,则显示标签,并显示模型的轮廓,反之移除模型轮廓显示和标签
window.addEventListener('click', (event) => {
// 计算鼠标点击位置在画布上的位置
const x = (event.clientX / window.innerWidth) * 2 - 1;
const y = -(event.clientY / window.innerHeight) * 2 + 1;
// 创建一个射线
const raycaster = new THREE.Raycaster();
// 设置射线从相机发出,并指向屏幕上的坐标(x, y)
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
// 检测射线与gltf场景中的物体是否相交,返回相交的物体数组
const intersects = raycaster.intersectObjects(gltf.scene.children, true);
if(intersects.length > 0) {
chooseObj = intersects[0].object;
intersects[0].object.add(tag);
outlinePass.selectedObjects = [ intersects[0].object ];
}else {
// 移除
outlinePass.selectedObjects = [];
chooseObj.remove(tag);
}
})
CSS3DRenderer
CSS3DRenderer
是一个能将普通网页元素(如文字、图片、表格)变成 3D 对象的特殊工具。它类似给网页内容套上一层“魔法外衣”,让这些二维元素能在三维空间中自由变换位置和角度
CSS3DRenderer
同CSS2DRenderer
使用方法一样
const loader = new GLTFLoader(loadingManager);
loader.load('model/electrical_box_-_free/scene.gltf', function (gltf) {
const div = document.getElementById('tag');
const tag = new CSS3DObject(div);
div.style.pointerEvents = 'none';
tag.scale.set(0.01, 0.01, 0.01);
tag.position.set(0, 2.1, 0);
gltf.scene.add(tag);
scene.add(gltf.scene);
})
css3Renderer = new CSS3DRenderer();
css3Renderer.setSize(window.innerWidth, window.innerHeight);
css3Renderer.domElement.style.position = 'absolute';
css3Renderer.domElement.style.top = 0;
css3Renderer.domElement.style.pointerEvents = 'none';
css3Renderer.render(scene, camera);
document.body.appendChild(css3Renderer.domElement);
与CSS2DRenderer区别
特性 | CSS3DRenderer | CSS2DRenderer |
---|---|---|
缩放支持 | ✔️ 标签随场景缩放自动调整大小 | ❌ 标签固定像素大小 |
旋转效果 | ✔️ 支持三维旋转 | ❌ 仅支持平移 |
适用场景 | 需要复杂交互的动态标签 | 静态标签(如简单注释) |
适用场景
- 数据可视化:在 3D 图表旁悬浮显示数据详情
- 虚拟展厅:展品旁弹出交互式介绍卡片
- 教育工具:分子结构模型中动态标注化学成分
批量标注
这里使用CSS3DRenderer
来进行批量标注,和CSS2DRenderer
实现思路是一样的
const loader = new GLTFLoader(loadingManager);
loader.load('model/sewing_factory/scene.gltf', function (gltf) {
gltf.scene.traverse((child) => {
if (child.isMesh) {
child.position.set(Math.random() * 50, 0, Math.random() * 30);
const div = document.getElementById('tag').cloneNode();
div.innerHTML = child.name; // 设置标签内容为模型名称
const tag = new CSS3DObject(div);
tag.scale.set(0.1, 0.1, 0.1);
tag.position.set(0,10,0);
div.style.pointerEvents = 'none';
child.add(tag);
}
})
scene.add(gltf.scene);
gltf.scene.position.set(0, 0, 0);
});
精灵模型渲染标签
精灵模型(Sprite) 实现的标签效果,始终面向屏幕,使用精灵模型来制作标签有以下几个特征
- 永远正对屏幕
无论场景如何旋转,标签始终正对用户,类似公告牌效果 - 轻量高效
相比 3D 文字(TextGeometry
),Sprite 基于 2D 贴图渲染,性能更好 - 灵活定制
支持图片、文字贴图,甚至动态生成的 Canvas 内容
加载纹理,创建精灵模型并绑定到模型上
const texLoader = new THREE.TextureLoader();
const texture = texLoader.load('images/common/warning.png');
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(4, 3, 3);
sprite.position.set(0, 10, 0);
child.add(sprite);
与其他方式实现对比
方案 | 优势 | 缺点 |
---|---|---|
精灵模型(Sprite) | 性能高、支持动态内容、始终面向屏幕 | 文字需预生成贴图,缩放可能模糊 |
CSS3DRenderer | 支持 HTML 元素、样式更灵活 | 性能略低,复杂旋转时位置易偏移 |
3D 立体文字 | 有立体感、可三维旋转 | 加载字体文件耗资源,性能差 |
适用场景
- 设备监控面板:在传感器模型旁显示实时数据
- 地图标注:标记建筑物的名称或状态
- 游戏 NPC 对话:角色头顶显示对话气泡
实际开发中建议使用CSS3DRenderer
处理大量动态标签,使用Sprite
处理简单静态标签