基于Three.js实现高度图地形等高线及相似高度区域选择咨询
实现基于Three.js的地形等高线与高度选中工具方案
一、核心概念解析
1. 等高线(Contour Line)
- 定义:地形上海拔高度相同的点连接成的闭合曲线,直观展示地形起伏。
- 用途:给地形Mesh叠加高度相同的线条,让地形层级更清晰,匹配你要的参考图效果。
- 拓扑作用:能直观呈现地形的坡度、山谷、山脊等特征,但无法表达地形的层级嵌套关系(比如大坑里的小坑)。
2. 等高树(Contour Tree)
- 定义:把等高线按海拔高低和拓扑嵌套关系组织成树状结构,每个节点代表一条等高线,边代表等高线之间的包含/相邻关系。
- 用途:如果需要分析地形的拓扑结构(比如找出所有独立山谷、山峰,判断某条等高线所属地形单元)时会用到;但如果只是做可视化和点击选同高度区域,初期可以不用,先聚焦等高线实现。
- 拓扑作用:解决等高线无法表达的层级依赖问题,适合复杂地形的拓扑分析,比如自动识别河流流域、地形分区。
二、可行实现方案
1. 基于高度图生成地形Mesh(已有基础优化)
确保高度图数据转为顶点高度数组,用THREE.PlaneGeometry创建平面后,遍历顶点设置z轴高度:
// heightData为高度图像素灰度数组(0-255),plane为PlaneGeometry实例 const vertices = plane.attributes.position.array; const maxHeight = 50; // 自定义地形最大高度 for (let i = 0; i < vertices.length; i += 3) { const xIndex = Math.floor((i/3) % plane.parameters.widthSegments); const yIndex = Math.floor((i/3) / plane.parameters.widthSegments); const height = heightData[yIndex * plane.parameters.widthSegments + xIndex] / 255 * maxHeight; vertices[i + 2] = height; } plane.attributes.position.needsUpdate = true; plane.computeVertexNormals();
2. 生成等高线(两种核心方式)
方式一:Shader实现(性能优先,实时渲染)
通过片元着色器判断顶点高度,直接渲染等高线,适合大尺寸地形:
// 片元着色器代码 uniform float contourInterval; // 等高线间距 uniform float contourWidth; // 等高线宽度 uniform vec3 contourColor; // 等高线颜色 varying vec3 vPosition; // 顶点位置传递到片元 void main() { float height = vPosition.z; float fractional = fract(height / contourInterval); // 计算等高线的透明度权重 float lineWeight = smoothstep(0.0, contourWidth / contourInterval, fractional) - smoothstep(1.0 - contourWidth / contourInterval, 1.0, fractional); vec3 terrainColor = vec3(0.3, 0.6, 0.2); // 基础地形色 vec3 finalColor = mix(terrainColor, contourColor, lineWeight); gl_FragColor = vec4(finalColor, 1.0); }
- 优点:GPU加速,实时流畅;缺点:无法获取等高线几何数据,不支持交互选中。
方式二:几何生成等高线(支持交互)
遍历地形三角面,计算与等高高度的交点,生成可交互的线条:
function generateContours(terrainGeometry, contourHeights) { const contours = {}; const positions = terrainGeometry.attributes.position.array; const indices = terrainGeometry.index.array; // 遍历所有三角面 for (let i = 0; i < indices.length; i += 3) { const getVertex = idx => ({ x: positions[idx*3], y: positions[idx*3+1], z: positions[idx*3+2] }); const v1 = getVertex(indices[i]); const v2 = getVertex(indices[i+1]); const v3 = getVertex(indices[i+2]); // 遍历每个等高高度 for (const h of contourHeights) { const intersections = []; // 判断边是否与等高线相交,计算交点 const checkEdge = (a, b) => { if ((a.z < h && b.z > h) || (a.z > h && b.z < h)) { const t = (h - a.z) / (b.z - a.z); intersections.push({ x: a.x + t*(b.x - a.x), y: a.y + t*(b.y - a.y), z: h }); } }; checkEdge(v1, v2); checkEdge(v2, v3); checkEdge(v3, v1); // 两个交点形成线段,加入对应等高线集合 if (intersections.length === 2) { if (!contours[h]) contours[h] = []; contours[h].push(...intersections); } } } // 转为Three.js Line对象 const contourObjects = []; for (const h in contours) { const geometry = new THREE.BufferGeometry().setFromPoints(contours[h]); const material = new THREE.LineBasicMaterial({ color: 0xffffff }); contourObjects.push(new THREE.LineSegments(geometry, material)); } return contourObjects; }
- 优点:可获取几何数据,支持交互;缺点:CPU计算,大尺寸地形需分块优化。
3. 点击选中同高度区域/等高线
方案一:Raycaster检测匹配
通过射线检测获取点击的地形高度,高亮同高度元素:
const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); const tolerance = 0.5; // 高度容差 window.addEventListener('click', (event) => { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObject(terrainMesh); if (intersects.length === 0) return; const targetHeight = intersects[0].point.z; // 高亮同高度地形顶点(需提前给geometry添加color属性,开启vertexColors) const colors = terrainMesh.geometry.attributes.color.array; for (let i = 0; i < colors.length; i += 3) { const height = terrainMesh.geometry.attributes.position.array[i+2]; if (Math.abs(height - targetHeight) < tolerance) { colors[i] = 1; colors[i+1] = 0; colors[i+2] = 0; // 红色高亮 } else { colors[i] = 0.3; colors[i+1] = 0.6; colors[i+2] = 0.2; // 恢复原地形色 } } terrainMesh.geometry.attributes.color.needsUpdate = true; // 高亮对应等高线(几何生成方式适用) contourObjects.forEach(line => { const lineHeight = line.geometry.attributes.position.array[2]; line.material.color.set(Math.abs(lineHeight - targetHeight) < tolerance ? 0xff0000 : 0xffffff); }); });
方案二:高度图像素匹配(高效)
点击地形时通过UV坐标找到对应高度图像素,遍历像素匹配后映射到地形顶点,适合大尺寸地形,性能优于遍历顶点。
4. 完整工具流程
- 上传高度图:用
input[type="file"]获取图片,通过Canvas读取像素转为高度数组。 - 生成地形Mesh:用高度数组创建带高度的PlaneGeometry。
- 生成等高线:根据需求选择Shader或几何方式。
- 交互功能:实现点击选中同高度区域/等高线,添加间距、容差调整UI控件。
内容的提问来源于stack exchange,提问作者Pravin Poudel
相关产品推荐
相关产品推荐

