Three.js 模型标签
在很多的实际的项目中,你可能需要给一个 Three.js 的模型添加标签,标签可以通过一个包含文字图形信息的 HTML 元素或者一个 three.js 的精灵模型来表示。
常见问题
- three.js 三维模型如何添加注释文字?
- Three.js 模型对象如何设置标签?
- three.js 如何用 HTML 元素设置显示热点
- 如何把模型的世界坐标转化为屏幕坐标?
每个人的基础不同,虽然知识点区别不大,可能描述的时候会有很大区别。
视频讲解和源码
下面对知识点简单介绍,关于视频讲解可以关注我博客发布可的相关课程,个人 WebGL/Three.js 技术博客。
层级模型
复杂的项目,一个 three.js 场景往往包含包含多个模型对象,模型对象也会有一些父对象,这样就会形成一个层级模型,从数据结构的角度来看就是树结构。
一个模型相对世界坐标系原点的位置是世界坐标,相对父对象的位置是局部坐标。
获取模型位置
你如果想给一个模型设置标签,首先需要获得模型在世界坐标系中所在的位置,如果你希望标注一个模型的某个局部位置,那就要获得该局部区域的一个顶点坐标。
-
.position
属性是一个模型的局部位置,相对父对象的位置,如果一个网格模型 Mesh 直接属于场景 Scene,没有除了 Scene 意外的父对象 -
.vertices
如果几何体是 Geometry 类型,可以通过.vertices
属性访问顶点,通过下标可以访问具体的顶点位置坐标。 -
.attributes.position
如果几何体是 BufferGeometry 类型,可以通过.attributes.position
属性访问顶点位置数据。 -
.getWorldPosition()
实际项目中一个模型对象可以有多个父对象,这个时候想获得该模型对象的世界坐标,就不能通过.position
属性,应该通过.getWorldPosition()
方法实现,该方法的使用参考基类Object3D
。
实际项目中获得一个模型,可能是通过点击获得,或者遍历对象根据属性值找到某个或某些模型,等等这里不展开说,这不是本节课的重点,关于递归遍历模型或者鼠标点击选中某个模型可以关注课程的其它部分。
精灵模型 Sprite
作为标签
在使用精灵模型表示标签之前,你应该先了解精灵模型 Sprite 有什么特点。使用精灵模型表示一个模型对象的标签,那么精灵模型就要位于模型对象的附近。可以获得要标注模型的世界坐标,然后来设置精灵标签的位置,适当偏移一点就可以,当然也可以把精灵对象插入到模型对象的父对象中,和模型对象一样作为父对象的子对象,这样的话如果模型父对象的位置变化,精灵模型可以跟着一起变化。
标签的样式可以让美术设计好直接作为精灵的贴图就可以,如果标签不是特定的,比如用户输入文字,平台自动生成模型标签,可以通过程序自动化合成一个纹理作为精灵模型的贴图,关于如何自动合成纹理贴图这里不详细阐述。
/**
* 创建点精灵模型
*/
// 创建精灵材质对象 SpriteMaterial
var spriteMaterial = new THREE.SpriteMaterial({map: new THREE.TextureLoader().load("立方体.png"), // 设置精灵纹理贴图
transparent: true,// 开启透明(纹理图片 png 有透明信息)
});
// 创建精灵模型对象,不需要几何体 geometry 参数
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(30, 30, 1); // 精灵大小
// 把精灵模型插入到模型对象的父对象下面
group.add(sprite);
// 父对象 group 位置变化, 网格模型及其对象的标签同样发生变化
group.position.set(10, 0, -80);
// 表示标签信息的精灵模型对象相对父对象设置一定的偏移
sprite.translateY(30);
div
等 html 元素作为标签(世界坐标转屏幕坐标)
通过 html 元素表示标签相比较使用精灵来说比较麻烦,需要进行坐标变换,一个模型显示在 Canvas 画布上,经过了相机的视图、投影变换,如果想把一个 div 元素标注在一个模型附近,就需要计算模型渲染到画布上的具体位置,也就是所谓的屏幕坐标。使用 html 元素的好处是可以直接输入汉字,利用 css 来设置一下标签的样式,当然也可以直接加载美术设计的标签。
世界坐标转 WebGL 标准设备坐标
- 世界坐标:世界坐标简单说就是模型在 three.js 三维空间中的位置,但是注意不是局部位置属性
.position
,你可以通过.getWorldPosition()
方法获得世界坐标,每个子对象都有一个相对父对象的位置,把一个对象的所有父对象相对 Scene 坐标原点的位置加起来就是一个模型的世界坐标 - 屏幕坐标:你可以理解为 Canvas 画布上的位置,单位是像素 px,这和 HTML 说的像素是一样的。
// 创建一个三维向量作为世界坐标
var worldVector = new THREE.Vector3();
// 获取网格模型 boxMesh 的世界坐标,赋值给 worldVector
boxMesh.getWorldPosition(worldVector);
// 世界坐标转标准设备坐标,standardVector 是 WebGL 设备坐标
var standardVector = worldVector.project(camera);
.project()
是向量 Vector3 的方法,一个表示位置的向量 Vector3 把相机作为参数执行该方法可以得到经过相机变换后的坐标。一般 threejs 渲染器渲染的时候,会自动从相机对象读取视图和投影矩阵对模型的顶点进行变换。
WebGL 标准设备坐标转屏幕坐标
Canvas 全屏显示的时候复合下面的计算规则,如果不是全屏要注意修改计算规则。如果不是全屏,计算 a 和 b 的值,不能使用 window.innerWidth,而应该使用 canvas 的具体宽高。如果 canvas 是局部显示相对 body 区域的左上角有偏移,那么 div 元素也要设置一定的偏移。
// 根据 WebGL 标准设备坐标 standardVector 计算 div 标签在浏览器页面的坐标
var a = window.innerWidth / 2;
var b = window.innerHeight / 2;
var x = Math.round(standardVector.x * a + a); // 标准设备坐标转屏幕坐标
var y = Math.round(-standardVector.y * b + b); // 标准设备坐标转屏幕坐标
/**
* 设置标签元素的位置
*/
div.style.left = x + 'px';
// 这里的 130px 主要是为了标签和模型有一定偏移,当然也可以不设置,两者叠加在一起
div.style.top = y - 130 + 'px';