|
|
@@ -341,12 +341,18 @@
|
|
|
const z = positionAttribute.getZ(i)
|
|
|
|
|
|
// 1. 获取热力值 (0 到 1 之间)
|
|
|
- const heatValue = generateHeatmapValue(x, z)
|
|
|
- heatmapValues.push(heatValue)
|
|
|
+ const originalHeatValue = generateHeatmapValue(x, z)
|
|
|
|
|
|
- // 2. 根据热力值和坐标生成地形高度
|
|
|
- // 热力值越高,地形越高,从而将颜色和高度绑定
|
|
|
- const y = heatValue * maxTerrainHeight + 15 // 整体抬高到建筑上方
|
|
|
+ // 为了让“红色区域不要太红”,我们对用于着色的热力值做轻微压缩/偏移处理:
|
|
|
+ // - 将热力值整体缩小到原来的 75%(降低最高端的红色强度)
|
|
|
+ // - 加上一个小的偏移以避免完全变暗
|
|
|
+ // - 最终上限设置为 0.95,避免出现完全饱和的红色
|
|
|
+ const colorHeatValue = Math.min(0.95, Math.max(0.0, originalHeatValue * 0.75 + 0.08))
|
|
|
+
|
|
|
+ heatmapValues.push(colorHeatValue)
|
|
|
+
|
|
|
+ // 2. 根据原始热力值和坐标生成地形高度(高度仍使用 originalHeatValue,使视觉的高低与热力对应)
|
|
|
+ const y = originalHeatValue * maxTerrainHeight + 15 // 整体抬高到建筑上方
|
|
|
|
|
|
positionAttribute.setY(i, y)
|
|
|
}
|
|
|
@@ -357,47 +363,61 @@
|
|
|
// 自定义着色器材质(与上次相同,但确保颜色映射和您图片一致)
|
|
|
const heatmapMaterial = new THREE.ShaderMaterial({
|
|
|
uniforms: {
|
|
|
- uTime: { value: 0.0 }
|
|
|
+ uTime: { value: 0.0 },
|
|
|
+ uOpacity: { value: 1.0 } // 全局不透明度控制,可在运行时调整
|
|
|
},
|
|
|
+ transparent: true, // 允许透明
|
|
|
+ depthWrite: false, // 关闭深度写入以便正确混合
|
|
|
+ blending: THREE.NormalBlending,
|
|
|
vertexShader: `
|
|
|
- attribute float heatValue;
|
|
|
- varying float vHeatValue;
|
|
|
-
|
|
|
- void main() {
|
|
|
- vHeatValue = heatValue;
|
|
|
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
- }
|
|
|
- `,
|
|
|
+ attribute float heatValue;
|
|
|
+ varying float vHeatValue;
|
|
|
+
|
|
|
+ void main() {
|
|
|
+ vHeatValue = heatValue;
|
|
|
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
+ }
|
|
|
+ `,
|
|
|
fragmentShader: `
|
|
|
- varying float vHeatValue;
|
|
|
-
|
|
|
- void main() {
|
|
|
- vec3 color = vec3(0.0);
|
|
|
-
|
|
|
- // 颜色映射逻辑: 确保渐变从深蓝 (最低值) 到红色 (最高值)
|
|
|
- if (vHeatValue < 0.25) {
|
|
|
- // 深蓝/黑蓝 到 蓝/青 (对应图中最低部分)
|
|
|
- color = mix(vec3(0.0, 0.1, 0.4), vec3(0.0, 0.5, 1.0), vHeatValue * 4.0);
|
|
|
- } else if (vHeatValue < 0.5) {
|
|
|
- // 蓝/青 到 绿色 (对应图中中间部分)
|
|
|
- color = mix(vec3(0.0, 0.5, 1.0), vec3(0.0, 1.0, 0.2), (vHeatValue - 0.25) * 4.0);
|
|
|
- } else if (vHeatValue < 0.75) {
|
|
|
- // 绿色 到 黄色 (对应图中高一些的部分)
|
|
|
- color = mix(vec3(0.0, 1.0, 0.2), vec3(1.0, 1.0, 0.0), (vHeatValue - 0.5) * 4.0);
|
|
|
- } else {
|
|
|
- // 黄色 到 红色/橙色 (对应图中最高峰部分)
|
|
|
- color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.2, 0.0), (vHeatValue - 0.75) * 4.0);
|
|
|
- }
|
|
|
-
|
|
|
- gl_FragColor = vec4(color, 1.0);
|
|
|
- }
|
|
|
- `,
|
|
|
+ varying float vHeatValue;
|
|
|
+ uniform float uOpacity;
|
|
|
+
|
|
|
+ void main() {
|
|
|
+ vec3 color = vec3(0.0);
|
|
|
+
|
|
|
+ // 颜色映射逻辑: 确保渐变从深蓝 (最低值) 到红色 (最高值)
|
|
|
+ if (vHeatValue < 0.25) {
|
|
|
+ // 深蓝/黑蓝 到 蓝/青 (对应图中最低部分)
|
|
|
+ color = mix(vec3(0.0, 0.1, 0.4), vec3(0.0, 0.5, 1.0), vHeatValue * 4.0);
|
|
|
+ } else if (vHeatValue < 0.5) {
|
|
|
+ // 蓝/青 到 绿色 (对应图中中间部分)
|
|
|
+ color = mix(vec3(0.0, 0.5, 1.0), vec3(0.0, 1.0, 0.2), (vHeatValue - 0.25) * 4.0);
|
|
|
+ } else if (vHeatValue < 0.75) {
|
|
|
+ // 绿色 到 黄色 (对应图中高一些的部分)
|
|
|
+ color = mix(vec3(0.0, 1.0, 0.2), vec3(1.0, 1.0, 0.0), (vHeatValue - 0.5) * 4.0);
|
|
|
+ } else {
|
|
|
+ // 黄色 到 红色/橙色 (对应图中最高峰部分)
|
|
|
+ color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.2, 0.0), (vHeatValue - 0.75) * 4.0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 基于热力值计算每片面的不透明度:低热力值更透明,热值高则更不透明
|
|
|
+ float minA = 0.08;
|
|
|
+ float maxA = 0.95;
|
|
|
+ float localOpacity = mix(minA, maxA, vHeatValue);
|
|
|
+
|
|
|
+ // 将局部不透明度与全局控制相乘,方便外部动态调整整体透明度
|
|
|
+ float finalA = clamp(localOpacity * uOpacity, 0.0, 1.0);
|
|
|
+
|
|
|
+ gl_FragColor = vec4(color, finalA);
|
|
|
+ }
|
|
|
+ `,
|
|
|
side: THREE.DoubleSide
|
|
|
})
|
|
|
|
|
|
heatmapMesh = new THREE.Mesh(geometry, heatmapMaterial)
|
|
|
heatmapMesh.visible = false
|
|
|
heatmapMesh.scale.set(0.2, 0.2, 0.2)
|
|
|
+ heatmapMesh
|
|
|
scene.add(heatmapMesh)
|
|
|
}
|
|
|
|