欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Threejs官例解析,标签&css2dlabel(一)

程序员文章站 2022-05-27 16:14:47
...

最近需要给自个儿项目的模型加上标签,来显示一些有的没的的数据。

去例子萌里瞄了一眼,发现了css2dlabel 这个例子。

远瞅是这样的!其实近瞅也这样,这个顶着一个moon的月亮会绕着这个地球一直旋转,上面的标签也会和他一起移动。(我总感觉应该顶一个奋斗当标签)
Threejs官例解析,标签&css2dlabel(一)
Threejs官例解析,标签&css2dlabel(一)

闲言少叙,来看看咋用的。

调用篇

拿月球举例子吧

生成球。这个瞄一眼应该不会有太大的问题。

Threejs官例解析,标签&css2dlabel(一)
shininess。默认是30,看起来会有明显的高光点。而月球表面那么多石灰一样的东西,太亮不太真,所以调暗点。

下面是30和5的对比,大小应该一样,截屏没控制好。

Threejs官例解析,标签&css2dlabel(一)
Threejs官例解析,标签&css2dlabel(一)
生成标签如下:
Threejs官例解析,标签&css2dlabel(一)

正常生成一个文字为Moon的div,label类里有点样式,不多。要想丰富样式就多改改这里。

重点来了,他生成了一个CSS2DObject类,传入了那个div, 并把它举到了月亮头顶。加到了月亮坐标系。

如果新建了一个三维的无论什么东西,加到月亮里面都是会跟着月亮一起动的。

可是,我们加的是二维的。不在一个维度里。这也是这个例子最有趣的地方。

Init函数里示例化了一个CSS2DRenderer,这个类里面有一个超大的隐藏的div来管理所有的label,这个下面读源码的时候还会分析,先瞄一眼过,应该问题不大。
Threejs官例解析,标签&css2dlabel(一)

调用部分剩下就是,让月亮转转,刷新一下labelRenderer,最后这个才是这盘菜所有的精华。

Threejs官例解析,标签&css2dlabel(一)
核心篇

这里总管两个类,一个CSS2DObject,一个CSS2DRenderer

CSS2DObject 这个类没有太大的用处,基本是用来类型识别,本身也很简单

Threejs官例解析,标签&css2dlabel(一)
用来类型识别

上面的这个监听很有趣,不知道干嘛的,印象里也没有这个监听,难道要自定义使用?类似于下面这样?参考代码来自:这个小哥

window.addEventListener("testEvent", function(obj){
       console.log(obj.data);
       alert("触发成功!")
});
function clickHere() {
     // 创建自定义事件
     var event = document.createEvent("HTMLEvents");
     // 初始化testEvent事件
     event.initEvent("testEvent", false, true);
     event.data = {"click":true};
     // 触发自定义事件
     window.dispatchEvent(event);
 }

查了一下Dom事件也没发现。

CSS2DRenderer类里面负责了主要的操作和更新。

先看一下这个类里面的定义的变量
Threejs官例解析,标签&css2dlabel(一)

前面说过,这个里面会定义一个hidden的大的div,大概就是屏幕的分辨率,来管理所有的label,这里的长宽高都是这个div相关的属性。

vector记录投影变换后,在投影坐标系里面的坐标值。

研究清楚了这个我突然知道CSS2DObject这个类是干啥用的了,里面没有几行代码,我以为只是用来类型区分的,其实不是,我上面说,如果是一个三维物体扔在一个移动的父物体上,这个三维物体是会跟随父物体运动的,所以这货的意义就在于,它把一个label纯二维的东西,其实给包装成了一个三维的物体,让这个三维物体跟着父物体运动,反算这个三维物体的屏幕坐标,也就知道了label的屏幕偏移量。感觉好有趣,果然每个人实现的脑回路都是不一样的。如果是这样计算的话,其实直接计算实体的屏幕坐标也是可以的,比如那个月亮,在给一点二维上的偏移。

继续说这个vector,用到的就3行,还是蛮考验基础的。

Threejs官例解析,标签&css2dlabel(一)
如果一个物体是CSS2DObject的话,就从它的世界矩阵中获取这哥们的世界坐标。

顺便也可以把定义的viewProjectionMatrix变量也解释了。

我的一个 CSS2DObject 获得了世界坐标后,又转换到了相机的坐标系,然后进行了投影变换,可以简单理解为投影到了屏幕。这就是第二行做的事,最后一行,希望的尽量解释清楚。

translate(-50%,-50%) 这个变换如果不加,label偏右偏下,为了让它居中,加了这个。如下:

Threejs官例解析,标签&css2dlabel(一)Threejs官例解析,标签&css2dlabel(一)

后面的translate是为了将投影变换后的坐标,和屏幕的坐标相对应。如下:

Threejs官例解析,标签&css2dlabel(一)
Threejs官例解析,标签&css2dlabel(一)
我们有的是第一个坐标系的坐标,要获得第二个坐标系的,这个映射自己回去导,就是后半段的translate。

八卦一下viewProjectionMatrix矩阵是怎么获得的,
Threejs官例解析,标签&css2dlabel(一)

首先把scene里面的所有的物体撸一遍更一下世界坐标,然后更一下相机的世界坐标,

viewMatrix保存的是从世界坐标系转换到相机坐标系需要的矩阵。

在乘上projectionMatrix,就可以换到投影坐标系了~

(zOrder是自带的函数,rayObj是我自己根据需要加上的)

cache里的 WeakMap() 据说是ES6的新玩具,查了一下,瞄了一眼别人家的文章,主要的特点就以下几个:

key必须是object,且不可枚举,也就是说不可以被遍历。

没有size。

它的出现对垃圾回收友好,就是说没有引用的话可以袅袅悄悄的被回收。因为是“弱指针”。(最近满世界都对垃圾很关注嘛)

这个工程里是这样用滴。
Threejs官例解析,标签&css2dlabel(一)
Threejs官例解析,标签&css2dlabel(一)

还发现了一个好用的函数 scene.traverse 这家伙可以遍历 scene里面所有的物体。

周五没写完,隔了两天在写这篇像喝断片了一样。= =,看来还是要一鼓作气啊~

查了一下源码,发现这是Object3D.prototype里面的函数,大部分的类都是继承自这个类的,也就是说,大部分的实例都有这个函数。源码也不复杂,封装完的代码还是很清爽的。

Threejs官例解析,标签&css2dlabel(一)
因为项目的需要,在原有功能的基础上增加了遮挡隐藏和距离限制显示的功能。

简单来说就是如果地球挡住了月亮,就不显示月亮的标签。

如果在控制相机的过程中,和月球/地球的距离过近或过远都会隐藏标签。

有时候项目的标签不止一两个,甚至可能会很多,这样可以有效突出正在关注的标签。

增加了一个函数rayObj,接受四个参数(scene,camera, near,far)

最后两个如果不传参,相当于没有限制。

Threejs官例解析,标签&css2dlabel(一)
获得相机和含有CSS2DObject标签的物体的位置

Threejs官例解析,标签&css2dlabel(一)
判断是否在距离以内
Threejs官例解析,标签&css2dlabel(一)

判断是否有遮挡

Threejs官例解析,标签&css2dlabel(一)
从相机朝着物体发了一个射线,看看有没有遮挡。