3D温场效果
条评论效果
19年国庆节七天假里,花了两三天的时间去尝试研究一直没时间解决的3D温场问题,最终取得了一些效果:
具体完整代码与访问链接可看项目:https://github.com/alwxkxk/iot-visualization-examples
思路探索
寻找参考效果的例子
场早就有很多案例,通过关键词在google上搜索一些图片以供参考模仿。
实体效果
最早物色到的例子是在three.js example找到的:
分析了一下源码,主要原理就是每个点都拥有一个数值(在该例子里是压力值,实际上我们用来表示温度值也是一样的),根据数值来给予每个点不同的颜色,就能渲染出来这种效果。这种效果单体一个物体来展现的话,效果还行,就是计算量有点大可能需要后端直接计算好再传给前端。但细想了一下,如果是每个物体都是这种方式来展示温度,呈现到整个大场景来说是很丑的,如同这种效果:
单纯从观赏性来说,可以想象出这种效果最终会给人感觉很硬,并不好看,所以实体效果只适合给单一物体展示,不适合展示全景。
线框实体效果
类似于上面刚讨论的实体效果,只不过3D模型变成了线框模式。对于复杂的3D模型,线框模式是十分漂亮的:
实现原理初步估计也是跟实体效果差不多,根据每个点所对应的数值给予不同的颜色即可,只不过3D模型渲染成线框模式即可。这种效果对于展示单个复杂物体十分漂亮,在整个大场景下估计会好看那么一点点。
地板效果
上面讨论的效果都是立体的,而地板效果则是平面的。在大场景的展示多物体的情景下,感觉平面的效果远比立位效果要好看得多。
当然,这网上找来的效果图我是觉得有些问题的,就是温度传感器并不是整体空间都能覆盖的,没有温度传感器的地方自然没有数值,但也不能填充个默认的颜色上去呀,否则这一片绿色代表的到底是指温度正常呢,还是指没有传感器没有数值呢?所以实事求事地来讲,我觉得下面的效果才是相对合理的,没有数值的地方就该没有颜色。
我最终也是研究出了这种效果。
地板效果的实现思路
虽然3D温场的案例效果图网上很多,但相关代码实现原理的讨论倒是没找到,没办法复制粘贴别人的代码那么只能自己折腾了。关键步骤如下:
- 由heatmap.js生成温场图(canvas元素)。
- 创建新的地板平板并将温场图作为其纹理贴上去。
- 根据温度传感器的世界坐标投射到地板平面上得到其投射坐标,并转换计算出在canvas元素的坐标,根据传感器的温度值通过heatmap.js的API设置温场效果即可。
一开始我是想在地板的3D模型上添加这种温场效果,但发现实现起来还比较麻烦,折腾了几下也没成功做出来。后来灵感一现,就想到创建一个新平面来展现温场,实现起来简单多了。
heatmap.js生成温场图效果我就不多说了,直接看其官方文档即可。
创建新的地板平板并将温场图作为其纹理贴上去,这一步注意需要创建出来的平面的正面默认朝向是向着Z轴的正半轴方向,需要调整角度才能使其正面朝上。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const box3 = new THREE.Box3();
box3.setFromObject(mainObj);
// 创建出两倍大小的正方形作为地板温场的载体
const x = Math.abs(box3.max.x - box3.min.x);
const y = Math.abs(box3.max.z - box3.min.z);
const len = this.len = Math.max(x, y) * 2;
// texture 所展现的就是 对应的就是heatmap 生成 的 canvas。
const texture = new THREE.CanvasTexture(canvasElement);
const material = new THREE.MeshBasicMaterial({
transparent: true,
depthTest: false,
depthWrite: false,
map: texture,
});
const planeGeometry = new THREE.PlaneBufferGeometry( len, len );
const plane = this.plane = new THREE.Mesh(planeGeometry, material);
// 调整平板 角度,使其正面朝上展示温场效果,背面是透明的。
plane.rotation.x = - Math.PI * 0.5;
scene.add(plane);
相对于实体效果这种立体需要大量计算(需要给每个点一个计算出对应的颜色值),地板效果所需的计算量就少多了,可以直接在前端计算,只需要把每个温度传感器空间坐标投射到地板平面上就可以了。(传感器的世界坐标假设为(10,15,20),那么投影到地板XZ平面(即Y=0),其投射坐标就是(10,0,20))
比较核心的问题就是,如何由3D里的投影坐标能转换到canvas坐标上。通过计算与试验,由平板的坐标体系(平板中心为原点,向左上增长),结合UV图的坐标体系(UV图是从左下为原点,向右上增长),结合geometry.attributes.uv
的与其对应的坐标,结合heatmap API的坐标体系(左上为原点,向右下增长),计算投影坐标在平面的百分比,换算出在canvas元素的坐标,最终换算公式如下:1
2
3
4
5// v.x v.y 为投射坐标。
// this.len 为正式形平板的边长
// width height 为heatmap 创建 canvas的高宽
x: ( (v.x / this.len) + 0.5 ) * width,
y: ( (v.y / this.len) + 0.5 ) * height,
转换成canvas坐标后就能过后heatmap的API传入数值即可设置出效果。
完整的效果与项目代码可查看项目:https://github.com/alwxkxk/iot-visualization-examples