1.熱力圖html
開始的時候,是用一個網上找的canvas畫漸變熱點的demo,原理就是給定頂點座標,而後畫圓,顏色使用漸變色,根據權重決定漸變的層數(紅色->橙色->綠色) 。web
可是終究以爲這種方法不只繁瑣,並且畫出來的效果不夠天然。canvas
後來發現有一個開源庫heatmap效果很好,它是這樣用的(官方demo地址連接):api
var heatmapInstance = h337.create({ container: document.querySelector('.heatmap') }); var data = { max: max, data: points }; heatmapInstance.setData(data);
max值爲全部points中權重屬性的最大值。dom
看到這裏,那咱們要怎麼在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.軌跡線webgl
軌跡線不難想,利用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條線在一塊兒呢?只要間距足夠小,它們看上去就是一根線,一根麻繩!!!
照着這個思路, 寫了一個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++,而且判斷進度是否是到了指定的某個點上,若是到了,那麼就順便把這個點也畫出來,就像上述的動圖同樣。
最終可控制粗細和運動速度的曲線就完成了。