主要思路:
- geometry数据
- THREE.MeshPhongMaterial材质
- 使用threeLayer将geometry和material 组合成mesh
- 计算geometry.computeBoundingBox(),获取包围盒的尺寸
- 使用material.onBeforeCompile函数重写shader
- shader实现,根据时间判断当前片原位置与包围盒最上方的关系
1.geometry数据(数据过多,只展示一条)
const buildings = [
{
"type": "FeatureCollection",
"features": [
{
"properties" : {
"FID" : 0,
"Id" : 0,
"Floor" : 8,
"h" : "28"
},
"geometry" : {
"type": "Polygon",
"coordinates" : [
[
[
104.04551999800009,
30.594714973800023
],
[
104.04547719400011,
30.594788921700115
],
[
104.04559489400009,
30.59484406170003
],
[
104.04566980200008,
30.594723885400015
],
[
104.04596941700009,
30.594621694700038
],
[
104.04612992100004,
30.594685978100131
],
[
104.04617272999991,
30.594538174900038
],
[
104.04605502500004,
30.594529192499976
],
[
104.04605503200014,
30.594372247600063
],
[
104.04586242400001,
30.59437265720004
],
[
104.04586241699991,
30.594529602000136
],
[
104.04571261000007,
30.594576081600053
],
[
104.04560561099998,
30.594474757800015
],
[
104.04542370100006,
30.594622860000072
],
[
104.04551999800009,
30.594714973800023
]
]
]
}
},
]}]
2.材质
const material = new THREE.MeshPhongMaterial({
color: '#1B3045',
transparent: true,
needsUpdate: true,
side: THREE.DoubleSide
});
3.合并geometry和material
this._threeLayer为new ThreeLayer的实例
const polygons:any[] = []
buildings[0].features.forEach(feature => {
const height = Number(feature.properties.h)
const polygon = GeoJSON.toGeometry(feature.geometry);
polygon.setProperties({height:height});
polygons.push(polygon);
})
const mesh = this._threeLayer.toExtrudePolygons(polygons.slice(0, Infinity), { topColor: '#fff', interactive: false, bloom: false }, material);
4.计算computeBoundingBox
通过mesh.getObject3d()获取threejs原生object3d对象
object.geometry.computeBoundingBox();
object.geometry.computeBoundingSphere();
const {
geometry
} = object;
// 获取geometry的长宽高 中心点
const {
center,
radius
} = geometry.boundingSphere;
const {
max,
min
} = geometry.boundingBox;
// 计算包围盒尺寸,,,z+5的原因是 增加包围盒的高度,让后续的动画执行周期增长
const size = new THREE.Vector3(
max.x - min.x,
max.y - min.y,
max.z - min.z + 5
);
5.material.onBeforeCompile重写材质
this.time = {
value: 0
};
this.StartTime = {
value: 0
};
material.onBeforeCompile = (shader:any) => {
shader.uniforms.time = this.time;
shader.uniforms.uStartTime = this.StartTime;
shader.uniforms.uSize = {
value: size
}
// 上下扫描参数
shader.uniforms.uFlow = {
value: new THREE.Vector3(
1, // 0 1开关
0.1, // 范围
0.2 // 速度
)
};
shader.uniforms.uColor = {
value: new THREE.Color("#1B3045")
}
// 效果颜色
shader.uniforms.uFlowColor = {
value: new THREE.Color("#FFFFDC")
}
/**
* 对片元着色器进行修改
*/
const fragment = `
float distanceTo(vec2 src, vec2 dst) {
float dx = src.x - dst.x;
float dy = src.y - dst.y;
float dv = dx * dx + dy * dy;
return sqrt(dv);
}
float lerp(float x, float y, float t) {
return (1.0 - t) * x + t * y;
}
vec3 getGradientColor(vec3 color1, vec3 color2, float index) {
float r = lerp(color1.r, color2.r, index);
float g = lerp(color1.g, color2.g, index);
float b = lerp(color1.b, color2.b, index);
return vec3(r, g, b);
}
varying vec4 vPositionMatrix;
varying vec3 vPosition;
uniform float time;
// 扩散参数
uniform float uRadius;
uniform float uOpacity;
// 初始动画参数
uniform float uStartTime;
uniform vec3 uMin;
uniform vec3 uMax;
uniform vec3 uSize;
uniform vec3 uFlow;
uniform vec3 uColor;
uniform vec3 uCenter;
uniform vec3 uSwitch;
uniform vec3 uTopColor;
uniform vec3 uFlowColor;
uniform vec3 uDiffusion;
uniform vec3 uDiffusionCenter;
uniform float uHeight;
void main() {
`;
const fragmentColor = `
// #include <aomap_fragment>
// 给建筑设置从上到下的渐变颜色
float indexPct = vPosition.z / uSize.z;
//像素混合mix(x,y,a)-->x * (1 - a) + y * a
vec3 color = uColor;
// 根据时间和速度计算出当前扫描点的位置, 以上顶点为准
//求两个数相除的余数
float flowTop = mod(uFlow.z * time, uSize.z);
// 判断当前点是否在扫描范围内
if (flowTop > vPosition.z && flowTop - uFlow.y < vPosition.z) {
// 扫描范围内的位置设置从上到下的渐变颜色
float flowPct = (uFlow.y - ( flowTop - vPosition.z)) / uFlow.y;
color = mix(color ,uFlowColor, flowPct);
}
// gl_FragColor = vec4(color, 1.0);
vec4 diffuseColor = vec4( color, 1.0 );
`;
shader.fragmentShader = shader.fragmentShader.replace("void main() {", fragment)
shader.fragmentShader = shader.fragmentShader.replace("vec4 diffuseColor = vec4( diffuse, opacity );", fragmentColor);
/**
* 对顶点着色器进行修改
*/
const vertex = `
varying vec4 vPositionMatrix;
varying vec3 vPosition;
uniform float uStartTime;
void main() {
vPositionMatrix = projectionMatrix * vec4(position, 1.0);
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
`
const vertexPosition = `
vec3 transformed = vec3(position.x, position.y, position.z * uStartTime);
`
shader.vertexShader = shader.vertexShader.replace("void main() {", vertex);
shader.vertexShader = shader.vertexShader.replace("#include <begin_vertex>", vertexPosition);
}
6.最后执行动画函数
init() {
this.clock = new THREE.Clock()
this.isStart = true;
this.animate()
}
animate () {
const dt = this.clock ? this.clock.getDelta() : 0
this.time.value += dt;
// 启动
if (this.isStart) {
this.StartTime.value += dt * 0.5;
if (this.StartTime.value >= 1) {
this.StartTime.value = 1;
this.isStart = false;
}
}
return requestAnimationFrame(this.animate.bind(this))
}
maptalks+threejs建筑物上下扫描效果