|
|
@@ -30,7 +30,7 @@
|
|
|
import { onMounted, onUnmounted, provide, ref } from 'vue'
|
|
|
import { useOutsideHomeStore } from '@/stores/modules/home'
|
|
|
import { useOutsideSystemStore } from '@/stores/modules/system'
|
|
|
- import { getAutofitScale } from '@/utils'
|
|
|
+ import { getAutofitScale, $mitt } from '@/utils'
|
|
|
import storage from '@/utils/storage'
|
|
|
import RightClick from './components/rightClick.vue'
|
|
|
|
|
|
@@ -174,6 +174,8 @@
|
|
|
let axesHelper = null
|
|
|
let controls = null
|
|
|
let loader = null
|
|
|
+ let heatmapMesh // 用于控制热力图地形的 Mesh
|
|
|
+
|
|
|
const init = () => {
|
|
|
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)
|
|
|
// axesHelper = new THREE.AxesHelper(5)
|
|
|
@@ -249,6 +251,7 @@
|
|
|
scene.environment = texture
|
|
|
scene.background = 'transparent'
|
|
|
})
|
|
|
+ createHeatmapTerrain()
|
|
|
addEvent(renderer.domElement)
|
|
|
}
|
|
|
|
|
|
@@ -291,6 +294,116 @@
|
|
|
renderer.dispose() // 如果你的renderer对象有dispose方法的话
|
|
|
}
|
|
|
|
|
|
+ function generateHeatmapValue(x, z) {
|
|
|
+ // 增加 scale,使波纹更密集
|
|
|
+ const scale = 0.4
|
|
|
+
|
|
|
+ // 使用两个振幅不同的波叠加,创造更复杂的起伏
|
|
|
+ const value = Math.sin(x * scale) * 0.8 + Math.cos(z * scale * 1.5) * 1.2
|
|
|
+
|
|
|
+ // 我们想要热力值在中心区域更集中、更高。
|
|
|
+ // 使用高斯函数或类似的衰减函数,使中心点 (0, 0) 附近的值更高。
|
|
|
+ const distanceSq = x * x + z * z
|
|
|
+ const centerFactor = Math.exp(-distanceSq * 0.005) // 0.005 控制衰减速度
|
|
|
+
|
|
|
+ // 结合起伏和中心集中度
|
|
|
+ const combinedValue = (value + 2) * 0.5 * centerFactor
|
|
|
+
|
|
|
+ // 确保值在 0 到 1 之间
|
|
|
+ return Math.max(0.0, Math.min(1.0, combinedValue))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建热力图地形
|
|
|
+ function createHeatmapTerrain() {
|
|
|
+ // *** 调整尺寸: 进一步缩小到 35x35,并增加分段数使曲面更平滑 ***
|
|
|
+ const width = 35
|
|
|
+ const height = 35
|
|
|
+ const widthSegments = 80 // 增加分段数
|
|
|
+ const heightSegments = 80
|
|
|
+ const maxTerrainHeight = 4 // 增加地形最大高度,使起伏更剧烈
|
|
|
+
|
|
|
+ const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
|
|
|
+ geometry.rotateX(-Math.PI / 2)
|
|
|
+
|
|
|
+ const positionAttribute = geometry.getAttribute('position')
|
|
|
+ const heatmapValues = []
|
|
|
+
|
|
|
+ for (let i = 0; i < positionAttribute.count; i++) {
|
|
|
+ const x = positionAttribute.getX(i)
|
|
|
+ const z = positionAttribute.getZ(i)
|
|
|
+
|
|
|
+ // 1. 获取热力值 (0 到 1 之间)
|
|
|
+ const heatValue = generateHeatmapValue(x, z)
|
|
|
+ heatmapValues.push(heatValue)
|
|
|
+
|
|
|
+ // 2. 根据热力值和坐标生成地形高度
|
|
|
+ // 热力值越高,地形越高,从而将颜色和高度绑定
|
|
|
+ const y = heatValue * maxTerrainHeight + 15 // 整体抬高到建筑上方
|
|
|
+
|
|
|
+ positionAttribute.setY(i, y)
|
|
|
+ }
|
|
|
+
|
|
|
+ geometry.setAttribute('heatValue', new THREE.Float32BufferAttribute(heatmapValues, 1))
|
|
|
+ geometry.computeVertexNormals()
|
|
|
+
|
|
|
+ // 自定义着色器材质(与上次相同,但确保颜色映射和您图片一致)
|
|
|
+ const heatmapMaterial = new THREE.ShaderMaterial({
|
|
|
+ uniforms: {
|
|
|
+ uTime: { value: 0.0 }
|
|
|
+ },
|
|
|
+ vertexShader: `
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ `,
|
|
|
+ side: THREE.DoubleSide
|
|
|
+ })
|
|
|
+
|
|
|
+ heatmapMesh = new THREE.Mesh(geometry, heatmapMaterial)
|
|
|
+ heatmapMesh.visible = false
|
|
|
+ heatmapMesh.scale.set(0.2, 0.2, 0.2)
|
|
|
+ scene.add(heatmapMesh)
|
|
|
+ }
|
|
|
+
|
|
|
+ function toggleHeatmapVisibility(visible) {
|
|
|
+ if (heatmapMesh) {
|
|
|
+ heatmapMesh.visible = visible
|
|
|
+ if(visible){
|
|
|
+ translateCamera({ x: 0, y: 10, z: 20 }, { x: 0, y: 0, z: 0 }, 1)
|
|
|
+ }else{
|
|
|
+ translateCamera({ x: 0, y: 2, z: 10 }, { x: 0, y: 0, z: 0 }, 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
watch(
|
|
|
() => useHomeStore.temp,
|
|
|
(newV) => {
|
|
|
@@ -300,6 +413,12 @@
|
|
|
}
|
|
|
)
|
|
|
|
|
|
+ $mitt.on('onToolsIndex', (v) => {
|
|
|
+ if (v.type === 'rlt') {
|
|
|
+ toggleHeatmapVisibility(v.selected)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
init()
|
|
|
resize()
|