在很多的实际的项目中,你可能需要给一个Three.js的模型添加标签,标签可以通过一个包含文字图形信息的HTML元素或者一个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来设置一下标签的样式,当然也可以直接加载美术设计的标签。
.position
,你可以通过.getWorldPosition()
方法获得世界坐标,每个子对象都有一个相对父对象的位置,把一个对象的所有父对象相对Scene坐标原点的位置加起来就是一个模型的世界坐标//创建一个三维向量作为世界坐标
var worldVector = new THREE.Vector3();
//获取网格模型boxMesh的世界坐标,赋值给worldVector
boxMesh.getWorldPosition(worldVector);
//世界坐标转标准设备坐标,standardVector是WebGL设备坐标
var standardVector = worldVector.project(camera);
.project()
是向量Vector3的方法,一个表示位置的向量Vector3把相机作为参数执行该方法可以得到经过相机变换后的坐标。一般threejs渲染器渲染的时候,会自动从相机对象读取视图和投影矩阵对模型的顶点进行变换。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';