资讯动态

Three.js地形重建实战:从高度图到可交互3D地形

发布时间:2026/8/5 2:22:18 来源:尧图企业网站定制
1. 项目概述从平面到立体的Web地形革命如果你玩过《我的世界》或者看过一些科幻电影里的数字地球一定会对那些可以自由缩放、旋转、充满细节的三维地形感到好奇。在过去这类应用是大型游戏引擎或专业GIS软件的专属需要厚重的客户端和复杂的配置。但现在借助WebGL和Three.js我们完全可以在浏览器里用几行代码就重建出令人惊叹的真实地形。这不仅仅是“显示一张图片”而是创建一个具有真实高程数据、可交互、可漫游的立体世界。我最近完成了一个项目核心就是用Three.js加载并渲染了一片真实的山地地形。整个过程就像玩一场高级的数字积木我们拿到的是卫星或航测得到的“高度点阵图”DEM数据然后通过Three.js的魔法将这些二维的高度信息“拉”起来形成一个三维网格表面再贴上对应的卫星影像纹理一个活生生的微型数字沙盘就诞生了。这背后的技术栈非常清晰Three.js负责三维场景的组织和渲染WebGL是底层的图形API而GLSL着色器则是在GPU上为地形赋予光影和材质灵魂的关键。这项技术能做什么它的应用场景远超你的想象。除了显而易见的游戏和地图应用它还可以用于城市规划的方案预览、地质灾害的模拟分析、虚拟旅游导览甚至是教育领域的地理教学。对于前端开发者或创意程序员来说掌握Three.js地形重建就等于打开了一扇通往Web三维可视化深处的大门。无论你是想做一个炫酷的个人作品集背景还是开发严肃的商业应用这套流程都是核心基础。接下来我将拆解整个实现过程从数据获取到最终渲染分享我踩过的坑和总结的技巧。2. 核心思路与方案选型为什么是Heightmap Displacement在三维图形学中创建地形有多种方法比如程序化噪声生成Perlin Noise、手动建模、或者直接使用三角网格模型。但对于“重建真实地形”这个目标高度图位移Heightmap Displacement是性价比最高、最通用的方案。它的核心思想非常直观用一张灰度图高度图的像素亮度来决定一个平面网格上每个顶点在Y轴方向上的位移量。2.1 高度图Heightmap原理剖析你可以把高度图想象成一张地理区域的“指纹”。这张图通常是单通道的灰度图图像上的每一个像素对应真实世界中的一个坐标点。像素的颜色值从0到255的灰度映射为该点的高度值。纯黑色0代表最低点比如海平面纯白色255代表最高点比如山峰。通过读取这张图片我们就能知道这片区域上任意一点是山脊、山谷还是平原。选择这种方案有三大优势数据源丰富且标准真实地形的数字高程模型DEM数据如SRTM、ASTER GDEM很容易从NASA、USGS等机构免费获取并且其标准输出格式就是GeoTIFF或ASCII网格可以轻松转换为程序可读的灰度图。资源消耗可控地形细节的丰富度直接由高度图的分辨率和基础网格的细分程度决定。你可以根据性能需求灵活调整网格密度在视觉效果和渲染效率之间取得平衡。渲染管线友好位移计算既可以在CPU端预处理生成顶点数据也可以在GPU端通过顶点着色器实时计算。后者性能更佳能实现动态的地形变形效果。2.2 Three.js中的实现路径选择在Three.js中基于高度图创建地形主要有两种实现路径路径一几何体顶点预处理CPU端这是最直接的方法。我们创建一个PlaneGeometry平面几何体设置足够的宽度和高度分段数。然后遍历其每一个顶点根据顶点在平面上的x, z坐标去采样高度图中对应像素的灰度值将这个灰度值转换为高度Y值并直接修改该顶点的y属性。最后需要调用geometry.computeVertexNormals()重新计算法线以确保光照正确。注意这种方法在初始化时一次性计算所有顶点位置适合静态地形。当地形网格非常密集时初始化的计算量会较大可能引起页面短暂卡顿。路径二顶点着色器位移GPU端这是一种更高级、性能通常也更好的方法。我们创建一个ShaderMaterial自定义着色器材质在顶点着色器Vertex Shader中编写GLSL代码。我们依然需要一个平面网格作为基底但它的顶点Y坐标初始为0。在着色器中我们传入高度图作为纹理根据顶点的世界坐标或UV坐标去采样高度纹理获取高度值然后直接gl_Position.y height。所有的计算都在GPU上并行完成效率极高。实操心得GPU位移的优点是能实现动态地形如实时抬升、LOD细节层次更容易结合且不占用主线程。缺点是着色器编写有门槛且顶点数据本身没有“真实”的高度对于需要基于精确顶点位置进行物理碰撞检测的场景可能需要额外处理。对于大多数入门和中级应用我推荐从路径一开始。它逻辑清晰易于调试能帮助我们牢固理解高度图与几何体之间的关系。本教程也将主要围绕这种方法展开并在最后会简要介绍着色器方法的思路。2.3 工具与数据准备清单在开始写代码前我们需要准备好以下“食材”Three.js库通过npm安装或CDN引入最新稳定版。一张高度图Heightmap这是地形的骨骼。可以从 Terragen 这类软件导出或从 OpenTopography 下载真实DEM数据并利用QGIS等软件处理成灰度PNG。一张漫反射贴图Diffuse Map这是地形的皮肤。通常是卫星影像或人工绘制的彩色纹理图其UV需要与高度图严格对齐。可选一张法线贴图Normal Map用于模拟高频细节让低多边形地形看起来有岩石、草地的细微凹凸感无需增加几何复杂度。一个本地开发服务器因为涉及加载本地图片纹理直接打开HTML文件会有CORS限制。使用VSCode的Live Server插件或http-server包即可。3. 从零构建高度图加载与几何体生成让我们进入实战环节。假设我们已经准备好了一张512x512像素的高度图heightmap.png和一张同样尺寸的纹理图terrain_texture.jpg。3.1 初始化Three.js场景基础首先搭建一个标准的Three.js场景包括渲染器、相机、灯光和控制器。import * as THREE from three; import { OrbitControls } from three/addons/controls/OrbitControls.js; // 创建场景、相机、渲染器 const scene new THREE.Scene(); scene.background new THREE.Color(0x87ceeb); // 天空蓝背景 const camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 50, 100); const renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); // 添加轨道控制器方便用鼠标交互 const controls new OrbitControls(camera, renderer.domElement); controls.enableDamping true; // 平滑阻尼感 // 添加全局光源 const ambientLight new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); const directionalLight new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(100, 100, 50); scene.add(directionalLight);3.2 核心函数将高度图转换为地形几何体接下来是核心部分。我们将编写一个函数它接收一个THREE.Texture对象高度图纹理作为输入输出一个修改后的THREE.PlaneGeometry。function createTerrainFromHeightmap(heightmapTexture, options {}) { const { width 100, // 地形平面宽度 height 100, // 地形平面深度 widthSegments 255, // 宽度分段数建议为2的n次方-1便于与纹理像素对齐 heightSegments 255, // 深度分段数 maxHeight 20 // 最大高度缩放 } options; // 1. 创建基础平面几何体 const geometry new THREE.PlaneGeometry(width, height, widthSegments, heightSegments); // 将平面绕X轴旋转-90度使其正面朝上默认平面是竖着的 geometry.rotateX(-Math.PI / 2); const positionAttribute geometry.attributes.position; const count positionAttribute.count; // 顶点总数 // 2. 创建临时Canvas来读取高度图像素数据 // 因为Three.js的Texture默认是WebGL纹理我们无法直接读取像素。 // 需要将其绘制到2D Canvas上再通过getImageData读取。 const canvas document.createElement(canvas); const context canvas.getContext(2d); // 假设我们知道纹理图片的尺寸这里用原图尺寸。更严谨的做法是从image属性获取。 const img heightmapTexture.image; canvas.width img.width; canvas.height img.height; context.drawImage(img, 0, 0); const imageData context.getImageData(0, 0, canvas.width, canvas.height); const data imageData.data; // RGBA一维数组 // 3. 遍历所有顶点根据UV坐标采样高度图修改Y坐标 const uvAttribute geometry.attributes.uv; // 每个顶点对应的UV坐标 for (let i 0; i count; i) { const u uvAttribute.getX(i); // UV坐标的U分量范围[0, 1] const v uvAttribute.getY(i); // UV坐标的V分量范围[0, 1] // 将UV坐标映射到图像像素坐标 const xImg Math.floor(u * (canvas.width - 1)); const yImg Math.floor((1 - v) * (canvas.height - 1)); // 注意图像Y轴与UV V轴方向通常相反 // 计算在一维数组data中的索引RGBA四个值一组 const index (yImg * canvas.width xImg) * 4; // 取红色通道值作为高度灰度图RGB const pixelValue data[index]; // 将像素值0-255映射到高度范围0 - maxHeight // 这里使用简单的线性映射。有时为了突出地形特征会使用非线性函数。 const vertexHeight (pixelValue / 255) * maxHeight; // 修改该顶点的Y坐标 positionAttribute.setY(i, vertexHeight); } // 4. 至关重要更新几何体 positionAttribute.needsUpdate true; // 告诉Three.js顶点数据已变 geometry.computeVertexNormals(); // 根据新的顶点位置重新计算法线光照才正确 return geometry; }关键点解析与避坑指南UV坐标与图像坐标的转换几何体的UV坐标原点(0,0)通常在左下角而2D Canvas图像数据的原点在左上角。这就是为什么在计算yImg时用了(1 - v)来进行Y轴翻转。如果忽略这一点生成的地形将是上下颠倒的。高度映射策略(pixelValue / 255) * maxHeight是最简单的线性映射。但真实地形中高度分布可能不均匀。你可以引入一个曲线函数如二次函数、指数函数来调整让低海拔区域更平缓高海拔区域更陡峭从而增强视觉冲击力。例如vertexHeight Math.pow(pixelValue / 255, 2) * maxHeight。性能考量顶点遍历是CPU密集型操作。对于256*25665536个顶点的网格循环65536次并执行计算是可以接受的。但如果网格达到1024*1024就会明显卡顿。这时就必须考虑使用Worker在后台线程处理或者转向GPU着色器方案。法线计算computeVertexNormals()是必须的。它根据顶点位置变化重新计算每个顶点垂直于三角形面的方向向量。没有正确的法线你的地形在光照下会看起来像一块平坦、奇怪的塑料布失去所有的立体感。3.3 创建材质并生成网格有了几何体我们需要为其穿上“衣服”——材质。// 加载高度图和漫反射贴图 const textureLoader new THREE.TextureLoader(); const heightmapTexture textureLoader.load(assets/heightmap.png); const diffuseTexture textureLoader.load(assets/terrain_texture.jpg); // 通常需要重复贴图来覆盖更大区域这里假设纹理与地形1:1匹配 diffuseTexture.wrapS diffuseTexture.wrapT THREE.ClampToEdgeWrapping; // 创建地形几何体 const terrainGeometry createTerrainFromHeightmap(heightmapTexture, { width: 200, height: 200, widthSegments: 255, heightSegments: 255, maxHeight: 30 }); // 创建基础材质 const terrainMaterial new THREE.MeshStandardMaterial({ map: diffuseTexture, // 颜色贴图 // roughness: 0.8, // 可以调整粗糙度 // metalness: 0.2, // 可以调整金属度 side: THREE.DoubleSide // 双面渲染如果相机可能看到背面的话 }); // 创建网格对象并加入场景 const terrainMesh new THREE.Mesh(terrainGeometry, terrainMaterial); scene.add(terrainMesh);至此一个基本的三维地形已经出现在场景中了。你可以通过OrbitControls用鼠标拖拽、缩放、旋转来从各个角度观察它。4. 效果增强光影、细节与后期处理一个只有基础颜色贴图的地形看起来可能还有些“玩具感”。接下来我们通过几个步骤为其注入灵魂。4.1 添加法线贴图提升表面细节法线贴图是一种在不增加多边形数量的前提下模拟表面凹凸细节的技术。它通过改变像素的法线方向来影响光照计算。对于地形一张好的法线贴图可以让岩石的裂隙、草地的起伏瞬间显现。// 加载法线贴图通常由高度图在Photoshop或工具中生成 const normalTexture textureLoader.load(assets/terrain_normal.jpg); normalTexture.wrapS normalTexture.wrapT THREE.ClampToEdgeWrapping; // 更新材质 terrainMaterial.normalMap normalTexture; terrainMaterial.normalScale.set(1, 1); // 缩放法线贴图强度可以调成(2,2)增强效果注意法线贴图的RGB通道分别对应着法线向量的X、Y、Z分量。确保你的法线贴图是切线空间Tangent Space法线贴图这是Three.js默认使用的格式。如果效果看起来不对凹凸方向反了可以尝试将normalScale的y分量设为-1或者检查法线贴图生成过程。4.2 实现基于高度的渐变材质Vertex Colors我们常常希望山巅是白雪山腰是岩石山谷是草地。这种效果可以通过**顶点颜色Vertex Colors**来实现。我们在修改顶点高度时同时根据高度值为顶点赋予不同的颜色。// 在createTerrainFromHeightmap函数的顶点遍历循环内添加 const color new THREE.Color(); geometry.setAttribute(color, new THREE.BufferAttribute(new Float32Array(count * 3), 3)); const colorAttribute geometry.attributes.color; for (let i 0; i count; i) { // ... 之前的高度计算代码 ... const vertexHeight (pixelValue / 255) * maxHeight; positionAttribute.setY(i, vertexHeight); // 根据高度设置顶点颜色 let hue; if (vertexHeight maxHeight * 0.3) { hue 0.3; // 低处绿色 (HSV色相0.3约等于绿色) } else if (vertexHeight maxHeight * 0.7) { hue 0.1; // 中部棕色 } else { hue 0.0; // 高处红色模拟岩石或白色需额外处理 } color.setHSL(hue, 0.8, 0.5 * (0.2 vertexHeight / maxHeight)); // 亮度也随高度变化 colorAttribute.setXYZ(i, color.r, color.g, color.b); } // 循环结束后 colorAttribute.needsUpdate true; // 然后将材质改为支持顶点颜色的类型例如MeshLambertMaterial或MeshStandardMaterial并启用vertexColors const terrainMaterial new THREE.MeshStandardMaterial({ map: diffuseTexture, vertexColors: true, // 启用顶点颜色 // 顶点颜色会与map贴图颜色混合可以通过调整mix比例控制 });这样地形就会呈现出平滑的色彩渐变。你也可以定义更复杂的色带Color Ramp实现更精确的生态分区效果。4.3 添加雾效与天空盒雾效Fog能极大地增强场景的纵深感让远处的地形逐渐融入背景色。// 添加指数雾效参数为颜色、近端浓度、远端浓度 scene.fog new THREE.FogExp2(0x87ceeb, 0.01);天空盒Skybox则提供了一个连贯的背景。你可以使用六张图片构成立方体贴图或者使用一个巨大的球体/立方体贴上无缝的星空或云层纹理。// 简单示例使用立方体贴图作为天空盒需要准备六张图px, nx, py, ny, pz, nz const cubeTextureLoader new THREE.CubeTextureLoader(); cubeTextureLoader.setPath(assets/skybox/); const skyboxTexture cubeTextureLoader.load([ px.jpg, nx.jpg, py.jpg, ny.jpg, pz.jpg, nz.jpg ]); scene.background skyboxTexture; // 直接设置为场景背景4.4 性能优化LOD与视锥体裁剪当地形非常大时渲染所有细节是浪费的。细节层次LOD技术可以根据物体与相机的距离切换不同精度的模型。Three.js提供了THREE.LOD对象。基本思路是为同一块地形创建高、中、低三个不同分段数的几何体版本。然后根据距离添加到LOD对象中。const lod new THREE.LOD(); // 创建高细节地形近处使用 const highDetailGeometry createTerrainFromHeightmap(heightmapTexture, { widthSegments: 255, heightSegments: 255 }); const highDetailMesh new THREE.Mesh(highDetailGeometry, terrainMaterial); lod.addLevel(highDetailMesh, 0); // 从距离0开始使用 // 创建中细节地形 const midDetailGeometry createTerrainFromHeightmap(heightmapTexture, { widthSegments: 127, heightSegments: 127 }); const midDetailMesh new THREE.Mesh(midDetailGeometry, terrainMaterial); lod.addLevel(midDetailMesh, 50); // 距离50时切换到这个 // 创建低细节地形 const lowDetailGeometry createTerrainFromHeightmap(heightmapTexture, { widthSegments: 63, heightSegments: 63 }); const lowDetailMesh new THREE.Mesh(lowDetailGeometry, terrainMaterial); lod.addLevel(lowDetailMesh, 150); // 距离150时切换 scene.add(lod); // 注意需要在动画循环中更新LOD调用 lod.update(camera);此外确保启用渲染器的视锥体裁剪Frustum Culling这是默认开启的。它会自动不渲染在相机视野外的物体。5. 进阶探索着色器位移与动态交互当我们掌握了基础方法后可以挑战更高效的GPU位移方案并增加交互性。5.1 顶点着色器位移实现我们不再在JavaScript中修改顶点而是将高度图传递给着色器让GPU去计算。// 1. 创建基础平面几何体顶点Y全为0 const geometry new THREE.PlaneGeometry(200, 200, 255, 255).rotateX(-Math.PI / 2); // 2. 创建自定义着色器材质 const vertexShader uniform sampler2D heightMap; uniform float uMaxHeight; varying vec2 vUv; varying float vElevation; void main() { vUv uv; // 采样高度图纹理纹理需要设置为nearestFilter以获得准确值 float height texture2D(heightMap, uv).r; // 取R通道 vElevation height * uMaxHeight; vec3 newPosition position; newPosition.y vElevation; // 直接修改Y坐标 gl_Position projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); } ; const fragmentShader uniform sampler2D diffuseMap; varying vec2 vUv; varying float vElevation; void main() { vec4 texColor texture2D(diffuseMap, vUv); // 可以基于vElevation在片段着色器中混合颜色 gl_FragColor texColor; } ; const terrainMaterial new THREE.ShaderMaterial({ uniforms: { heightMap: { value: heightmapTexture }, diffuseMap: { value: diffuseTexture }, uMaxHeight: { value: 30.0 } }, vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide }); // 3. 重要设置高度图纹理的采样方式避免插值 heightmapTexture.minFilter THREE.NearestFilter; heightmapTexture.magFilter THREE.NearestFilter;这种方法将所有计算压力转移到了GPU性能极佳并且为实现实时地形变形如水面上升、爆炸 crater打开了大门——你只需要在JavaScript中动态修改传递给着色器的uniform变量比如一个表示变形中心和大小的参数即可。5.2 添加水面与反射一个真实的地形场景常常包含水域。我们可以用另一个平面来代表水面并赋予它半透明和反射材质。// 创建水面几何体略大于或等于地形边界 const waterGeometry new THREE.PlaneGeometry(220, 220, 1, 1).rotateX(-Math.PI / 2); // 使用专门的水面材质或自定义着色器 const waterMaterial new THREE.MeshStandardMaterial({ color: 0x0077be, transparent: true, opacity: 0.8, roughness: 0.1, metalness: 0.9 }); const waterMesh new THREE.Mesh(waterGeometry, waterMaterial); waterMesh.position.y 5; // 设置水面高度比如在海平面5米处 scene.add(waterMesh);更高级的水面效果需要用到反射纹理THREE.WebGLCubeRenderTarget和法线贴图模拟波纹这涉及到渲染到纹理Render-to-Texture的技术复杂度会显著增加。5.3 实现简单的点击拾取与地形高度查询交互是三维场景的灵魂。我们可以通过THREE.Raycaster来实现从屏幕点击到三维世界的投射检测与地形的交点。const raycaster new THREE.Raycaster(); const mouse new THREE.Vector2(); function onMouseClick(event) { // 将鼠标点击位置归一化为设备坐标-1到1 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) { const point intersects[0].point; console.log(点击位置的世界坐标: (${point.x.toFixed(2)}, ${point.y.toFixed(2)}, ${point.z.toFixed(2)})); // 可以在这里放置一个标记如一个小球到point位置 addMarkerAt(point); } } window.addEventListener(click, onMouseClick);这个交点的point.y就是该点在地形上的精确高度。基于此你可以扩展出路径规划、建筑放置、地形分析等多种交互功能。6. 常见问题、排查技巧与性能调优实录在实际开发中你一定会遇到各种奇怪的问题。下面是我总结的一些典型坑位和解决方案。6.1 地形渲染问题排查表问题现象可能原因排查步骤与解决方案地形一片漆黑1. 光照未正确设置或强度太低。2. 法线计算错误或未计算。3. 材质类型不对如用了MeshBasicMaterial不受光。1. 检查场景中AmbientLight和DirectionalLight是否添加且强度合理。2. 确认在修改顶点后调用了geometry.computeVertexNormals()。3. 将材质切换为受光的类型如MeshLambertMaterial或MeshStandardMaterial。地形纹理拉伸或错乱1. 几何体UV与纹理尺寸不匹配。2. 高度图与漫反射贴图地理范围未对齐。3. 纹理wrap模式设置错误。1. 确保PlaneGeometry的尺寸比例与纹理图片的宽高比大致一致。2. 检查高度图和彩色图是否来源于同一区域且已配准。在Photoshop中叠加检查。3. 尝试将纹理的wrapS和wrapT设置为THREE.ClampToEdgeWrapping。地形边缘有裂缝或不平滑1. 高度图边缘像素值不为0黑色导致地形在边界处突然隆起或下陷。2. 平面几何体的分段数不足以表现边缘的平滑过渡。1. 在图像处理软件中确保高度图的四边是纯黑色0值或使用渐变工具柔化边缘。2. 增加PlaneGeometry的widthSegments和heightSegments。性能卡顿帧率低1. 顶点数量过多分段数太高。2. 纹理尺寸过大。3. 每帧进行了昂贵的CPU计算如更新顶点。4. 未启用硬件加速或浏览器限制。1. 降低网格分段数或实施LOD。2. 压缩纹理使用合适的尺寸如1024x1024而非4096x4096。3. 将静态地形的顶点计算移至初始化阶段避免在动画循环中重复计算。考虑使用GPU着色器方案。4. 检查浏览器控制台是否有WebGL错误。确保渲染器创建成功。控制台报错WebGL context lost通常是由于GPU内存不足或浏览器标签页休眠导致。1. 监听webglcontextlost事件尝试恢复或友好提示用户刷新页面。2. 优化资源及时销毁不再需要的几何体、纹理和材质调用.dispose()。3. 减少同时加载的高分辨率纹理数量。6.2 深度优化与调试技巧使用Stats.js监控性能引入stats.js库实时查看帧率FPS、渲染周期时间是性能调优的必备工具。纹理压缩与格式选择对于地形纹理使用.jpg格式通常比.png体积小很多。Three.js支持RGB_ETC1、RGBA_ASTC等压缩纹理格式可以大幅减少GPU内存占用但需要工具预处理。合并几何体如果你的场景由多块相同材质的地形块组成考虑使用THREE.BufferGeometryUtils.mergeBufferGeometries()将它们合并为一个几何体。这能显著减少绘制调用Draw Calls提升渲染效率。谨慎使用阴影renderer.shadowMap.enabled true会开启阴影计算消耗巨大。如果地形本身作为阴影接收者地面可以只让主要物体投射阴影到其上。考虑使用更高效的阴影类型如THREE.PCFSoftShadowMap。利用Chrome DevTools在开发者工具的“Performance”面板录制一段时间分析火焰图找到耗时最长的函数。在“Memory”面板拍摄堆快照检查是否存在内存泄漏未被释放的几何体、纹理。6.3 数据获取与处理实战建议免费高程数据源NASA的SRTM和ASTER GDEM是全球覆盖的免费数字高程模型。可以通过USGS EarthExplorer或OpenTopography网站下载。下载到的通常是GeoTIFF格式。数据处理流程使用QGIS开源或Global Mapper商业打开GeoTIFF文件。首先裁剪出你感兴趣的区域。然后使用软件的“导出”或“栅格计算”功能将高程值线性缩放到0-255的范围并输出为8位的灰度PNG图像。确保导出时选择“无压缩”或“低压缩”以避免引入插值误差。纹理配准从Google Earth Engine或Sentinel Hub可以获取同一区域的卫星影像。在QGIS中将卫星影像图层与高程数据图层叠加确保它们的地理坐标系完全一致然后以完全相同的范围和像素大小导出卫星影像作为漫反射贴图。从一张平淡无奇的灰度图片到浏览器中一个可以自由探索的立体世界这个过程充满了挑战与乐趣。我个人的体会是地形重建项目是学习Three.js和计算机图形学概念的绝佳练手场。它串联起了几何体、材质、纹理、着色器、光照、性能优化等几乎所有核心知识点。当你看到自己亲手从数据中“复活”的山川河流那种成就感是无可比拟的。最后一个小建议不要追求第一次就做出电影级的效果。从最简单的黑白高度图开始一步步添加纹理、光照、雾效每完成一步都测试并理解其原理。遇到问题多查阅Three.js官方文档和示例社区的活跃度非常高。祝你编码愉快早日构建出属于自己的数字奇境。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价