zoukankan      html  css  js  c++  java
  • three.js效果之热力图和轨迹线

    1.热力图

    开始的时候,是用一个网上找的canvas画渐变热点的demo,原理就是给定顶点坐标,然后画圆,颜色使用渐变色,根据权重决定渐变的层数(红色->橙色->绿色) 。

    但是终究觉得这种方法不仅繁琐,而且画出来的效果不够自然。

    后来发现有一个开源库heatmap效果很好,它是这样用的(官方demo地址链接):

    var heatmapInstance = h337.create({
        container: document.querySelector('.heatmap')
    });
    var data = {
        max: max,
        data: points
    };
    heatmapInstance.setData(data);

    max值为所有points中权重属性的最大值。

    看到这里,那我们要怎么在three.js中去使用heatmap呢,他是用dom去实例化heatmap对象的啊。

    不用担心,我们可以creatElement('div'),然后在这个dom对象上实例化heatmap对象,并且

          var canvas = heatmapdiv.getElementsByTagName('canvas')[0];

    获取绘制后的canvas对象。

            let heatMapGeo = new THREE.PlaneGeometry(120,90);
            let heatMapTexture = new THREE.Texture(canvas);
            let heatMapMaterial = new THREE.MeshBasicMaterial({
                map: heatMapTexture,
                transparent:true
            });
            heatMapMaterial.map.needsUpdate = true;
            var heatMapPlane = new THREE.Mesh(heatMapGeo,heatMapMaterial);
            heatMapPlane.position.set(0,0.1,0);
            heatMapPlane.rotation.x = -Math.PI/2;
            this.scene.add(heatMapPlane);

    这样,用heatmap绘制的热力图就添加到了three.js创建的场景中去了。

    2.轨迹线

    轨迹线不难想,利用three.js提供的曲线来绘制,但是会存在如下两个问题:

    q1.three.js的曲线貌似只能一次性整条绘制出来,没有api显示可以按百分比绘制曲线,所以只好自己写shader实现

    q2.webgl渲染器不支持线宽属性(three.MeshLine支持线宽,不过没有研究是否支持按百分比绘制);

    q3.着色器里面可以针对点设置pointsize来实现点的大小(间接实现曲线的宽度控制),但是点是二维的,默认存在于x-y平面,所以在x-z平面看的时候,如果点的数量不够多那么就会出现断断续续的效果,但是采样的点数量足够多又会影响性能。

    上述的问题不能解决的话,后续的曲线样式优化(渐变)就无从谈起。

    期间我想过,既然点存在于x-y平面,那么我们就将x-z平面的轨迹放到x-y平面来绘制,最后将这条线绕x轴旋转90度,但是因为对点进行处理的时候,首先正方形的点->圆点->渐变(抗锯齿),最后,结果如下:

    看着好像成功了,但是由于深度检测机制(现在想来,是不是可以设置取消这条线的深度检测机制)的存在,某些角度下,这条线的本质(n个大号的点拼接)就变得很明显了,你会明显地看到这条线是由进行抗锯齿处理后的无数个点组成。

    哎,好像又遇到困难了啊。

    后来一想,既然three.js中一条线很细,那么10条线,100条线在一起呢?只要间距足够小,它们看上去就是一根线,一根麻绳!!!

    PS:

    回过头来看我当时的这个处理思路,其实在性能上还是有很大的提升空间的,至少增加了内存消耗;

    当然后续查看meshline源码,你会发现他是将顶点作偏移绘制三角面来实现有宽度的“线”,并且每个顶点还传入了顶点的索引值属性,可用于进度的控制;

    照着这个思路, 写了一个FatLine类:

    import * as THREE from 'three'
    /**
     * Author:桔子桑
     * Time:2019.10.12
     */
    
    const vs =`
        varying vec3 iPosition;
    
        void main(){
            iPosition = vec3(position);
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x,0.2,position.z,1.0);
        }
    `;
    const fs = `
        uniform float time;
        varying vec3 iPosition;
        uniform float alpha;
    
        void main( void ) {
            if(iPosition.y > time){
                discard;
            }else{
                gl_FragColor = vec4(0.813,0.124,0.334,alpha); 
            }
    
        }
    `;
    
    function FatLine(vertices,width,scene){
        this.width = width;
        this.vertices = vertices;
        this.start = 0;
        this.scene = scene;
        this.linearr = [];
        this.lines = [];
    }
    function createMaterial(vs, fs, start) {
        var attributes = {};
        var uniforms = {
            time: {type: 'f', value: start},
            size:{type:'f',value:25.0},
        };
        var meshMaterial = new THREE.ShaderMaterial({
            uniforms: uniforms,
            defaultAttributeValues : attributes,
            vertexShader: vs,
            fragmentShader: fs,
            transparent: true
        });
        return meshMaterial;
    }
    
    FatLine.prototype.draw = function() {
        var size = this.vertices.length;
        var length = Math.floor(this.width/2);
        var vm = this;
        for(var j =0;j<length;j++){
            var lineadd = [];
            var linereduce = [];
            for ( var i = 0; i <size; i ++ ) {
                var Vector3 = this.vertices[i],
                x = Vector3.x,
                y = Vector3.y,
                z = Vector3.z;
                var zadd = z+j*0.001;
                var zreduce = z-j*0.001;
                lineadd.push( new THREE.Vector3(x,y,zadd ));
                linereduce.push( new THREE.Vector3(x,y,zreduce ));
            }
            this.linearr.push(lineadd);
            this.linearr.push(linereduce);
        };
        this.linearr.push(vm.vertices);
        var pointsize = this.vertices.length * 10;
        for(var k = 0,size=this.linearr.length;k<size;k++){
            var vertices = this.linearr[k];
            var alpha = (Math.floor(size/2) - Math.floor(k/2))/Math.floor(size/2);
            var curve = new THREE.CatmullRomCurve3(vertices);
            var geometry = new THREE.Geometry();
            geometry.vertices = curve.getPoints(pointsize);
            var material = createMaterial(vs,fs,vm.start);
            material.uniforms.alpha = {type:'f',value:alpha};
            var line = new THREE.Line(geometry, material);
            this.lines.push(line);
            this.scene.add( line );
        }
    }
    
    FatLine.prototype.animate = function(speed,callback){
        var time = this.lines[0].material.uniforms.time.value;
        for(var i = 0,length=this.lines.length;i<length;i++){
            var line = this.lines[i];
            line.material.uniforms.time.value +=speed||0.3;
        };
        if(callback){
            callback(time);
        }
    }
    
    export default FatLine;

    你可以看到,着色器中还又一个uniform变量time,这个是用来在FatLine开启动画的时候,随着时间的进展来逐步绘制的。

    ok,看到这你以为就完了?no!!!

    刚开始的时候,按照常规当time++的时候,在x-z平面上轨迹点,我们判断x<time是否来控制曲线的绘制进度,但是一个问题出现了,人员轨迹点可能出现在一个房间兜圈子的情况(实际也是如此),这样就会存在第2个点和第200个点都满足x<2.0,那么总不能根据时间,第2秒的时候,直接把200秒时候的点也绘制出来了吧,这是不符合常理的。

    在下班回家的路上,我想到了一个问题,在三维空间,一个点有(x,y,z)三个维度的坐标信息数据传进了着色器里面,但是我们的人员轨迹只会存在于场景的x-z平面,所以这个y坐标值在着色器里面是没有用到的,哈哈,那么这个y值可以充当时间维度值,第一个点y=1,第二个点y=2,第三个点y=3...,如此一来,当time++的时候,我们只要判断y<time就可以实现在时间维度上的控制了。

    并且FatLine的animate函数还提供了一个回调函数,参数值是当前的time值,所以你可以用这个time值来绘制具体的点:

         if(this.FatLine){
              function addpoint(time){
                for(var i = 0,length=vm.vertices.length;i<length;i++){
                  var point = vm.vertices[i];
                  if(Math.abs(point.y-time)< 0.1){
                    vm.addpoint(point.x,point.z);
                  }
                }
              }
              this.FatLine.animate(0.2,addpoint);
            }

    每一帧都会animate一下,也就是time++,并且判断进度是不是到了指定的某个点上,如果到了,那么就顺便把这个点也画出来,就像上述的动图一样。

    最终可控制粗细和运动速度的曲线就完成了。

  • 相关阅读:
    CF1458D
    CF1415F
    CF612F
    部分博客请移步Gitbub
    Vlc视频插件遮挡弹出框
    kubernetes容器编排YAML详解
    Kubernetes核心技术之Pod
    Kubeadm搭建K8S集群
    博客配套工程公开
    Modelsim联合Matlab搭建FPGA图像仿真平台
  • 原文地址:https://www.cnblogs.com/eco-just/p/11667985.html
Copyright © 2011-2022 走看看