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++,并且判斷進度是不是到了指定的某個點上,如果到了,那么就順便把這個點也畫出來,就像上述的動圖一樣。
最終可控制粗細和運動速度的曲線就完成了。
總結
以上是生活随笔為你收集整理的three.js效果之热力图和轨迹线的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python怎么画出圆润的曲线_利用py
- 下一篇: 根据数据库表gengxin实体类_ASP