尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

WebGL与WebGPU实战:43个案例从基础渲染到高级优化

WebGL与WebGPU实战:43个案例从基础渲染到高级优化
📅 发布时间:2026/7/23 4:59:29

最近在开发WebGL/WebGPU项目时,经常遇到各种技术难题和性能瓶颈,网上资料分散且不成体系。本文整合了43个实战案例,覆盖从基础渲染到高级优化的完整解决方案,包含Three.js、Unity WebGL、百度地图集成等热门场景,每个案例都提供可运行的代码示例和详细的排错指南。

无论你是前端开发者想要入门3D可视化,还是游戏开发者需要优化WebGL性能,都能从本文找到实用的解决方案。文章特别针对当前热门的WebGPU大模型渲染、点聚合优化、材质丢失等高频问题提供了专项突破方案。

1. WebGL与WebGPU技术背景与核心概念

1.1 WebGL技术概述

WebGL(Web Graphics Library)是一种基于OpenGL ES的JavaScript API,允许在浏览器中渲染交互式2D和3D图形。它直接利用GPU进行图形计算,为网页提供硬件加速的图形渲染能力。WebGL 1.0基于OpenGL ES 2.0,WebGL 2.0基于OpenGL ES 3.0,提供了更强大的功能和更好的性能。

WebGL的核心优势在于跨平台兼容性,几乎所有现代浏览器都支持WebGL 1.0,主流浏览器也基本支持WebGL 2.0。这使得开发者可以创建复杂的3D应用而无需用户安装额外插件。

1.2 WebGPU技术演进

WebGPU是下一代Web图形API,旨在提供更接近现代图形API(如Vulkan、Metal、DirectX 12)的性能和功能。与WebGL相比,WebGPU提供了更低的CPU开销、更好的多线程支持和更高效的资源管理。

从Three.js官方文档可以看出,WebGPURenderer已经成为WebGLRenderer的替代方案。WebGPURenderer能够针对不同的后端进行目标渲染,默认情况下,如果浏览器支持WebGPU,渲染器会尝试使用WebGPU后端,否则会回退到WebGL 2后端。

1.3 Three.js框架介绍

Three.js是目前最流行的WebGL/WebGPU JavaScript库,它封装了底层的图形API,提供了更友好的开发接口。Three.js支持场景图、材质系统、几何体、灯光、相机等完整的3D图形概念,大大降低了Web3D开发的入门门槛。

最新版本的Three.js已经全面支持WebGPU,开发者可以通过简单的配置切换渲染器,享受WebGPU带来的性能提升。

2. 环境准备与开发工具配置

2.1 浏览器环境要求

WebGL和WebGPU对浏览器环境有特定要求。WebGL 1.0需要浏览器支持OpenGL ES 2.0,WebGL 2.0需要支持OpenGL ES 3.0。WebGPU目前还在逐步推广中,需要Chrome 113+、Firefox Nightly或Safari 16.4+等较新版本的浏览器。

检查浏览器支持性的代码示例:

// 检查WebGL支持性 function checkWebGLAvailability() { const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); if (gl && gl instanceof WebGLRenderingContext) { console.log('WebGL 1.0 支持'); return true; } const gl2 = canvas.getContext('webgl2'); if (gl2 && gl2 instanceof WebGL2RenderingContext) { console.log('WebGL 2.0 支持'); return true; } console.log('WebGL 不支持'); return false; } // 检查WebGPU支持性 async function checkWebGPUAvailability() { if (!navigator.gpu) { console.log('WebGPU 不支持'); return false; } const adapter = await navigator.gpu.requestAdapter(); if (adapter) { console.log('WebGPU 支持'); return true; } console.log('WebGPU 不支持'); return false; }

2.2 开发环境搭建

推荐使用现代前端开发工具链,包括Node.js、npm/yarn包管理器,以及Vite、Webpack等构建工具。Three.js可以通过npm安装,也支持CDN直接引入。

package.json配置示例:

{ "name": "webgl-webgpu-demo", "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "three": "^0.158.0" }, "devDependencies": { "vite": "^5.0.0" } }

2.3 调试工具配置

Chrome DevTools提供了强大的WebGL调试功能,包括帧分析、着色器编辑和性能监控。安装Three.js DevTools扩展可以更方便地调试Three.js应用。

调试配置示例:

// 启用Three.js调试模式 import * as THREE from 'three'; // 在开发环境中启用调试 if (import.meta.env.DEV) { window.THREE = THREE; // 将THREE暴露到全局,方便调试 }

3. 基础渲染案例:从WebGL到WebGPU

3.1 基础WebGL渲染场景

创建一个基础的Three.js WebGL渲染场景,包含立方体、灯光和相机控制:

// 文件路径:src/scenes/basic-webgl-scene.js import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; export class BasicWebGLScene { constructor(container) { this.container = container; this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.init(); } init() { // 设置渲染器 this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x222222); this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; this.container.appendChild(this.renderer.domElement); // 设置相机 this.camera.position.set(5, 5, 5); this.camera.lookAt(0, 0, 0); // 添加轨道控制器 this.controls = new OrbitControls(this.camera, this.renderer.domElement); this.controls.enableDamping = true; // 添加灯光 const ambientLight = new THREE.AmbientLight(0x404040); this.scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(10, 10, 5); directionalLight.castShadow = true; this.scene.add(directionalLight); // 创建立方体 const geometry = new THREE.BoxGeometry(2, 2, 2); const material = new THREE.MeshStandardMaterial({ color: 0x00ff00, roughness: 0.5, metalness: 0.5 }); this.cube = new THREE.Mesh(geometry, material); this.cube.castShadow = true; this.scene.add(this.cube); // 添加网格地面 const floorGeometry = new THREE.PlaneGeometry(10, 10); const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.8, metalness: 0.2 }); this.floor = new THREE.Mesh(floorGeometry, floorMaterial); this.floor.rotation.x = -Math.PI / 2; this.floor.receiveShadow = true; this.scene.add(this.floor); // 窗口大小调整处理 window.addEventListener('resize', this.onWindowResize.bind(this)); // 开始动画循环 this.animate(); } onWindowResize() { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); } animate() { requestAnimationFrame(this.animate.bind(this)); // 立方体旋转动画 this.cube.rotation.x += 0.01; this.cube.rotation.y += 0.01; this.controls.update(); this.renderer.render(this.scene, this.camera); } }

3.2 WebGPU渲染器迁移

将上述WebGL场景迁移到WebGPU渲染器,体验性能提升:

// 文件路径:src/scenes/basic-webgpu-scene.js import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { WebGPURenderer } from 'three/examples/jsm/renderers/webgpu/WebGPURenderer.js'; export class BasicWebGPUScene { constructor(container) { this.container = container; this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); this.init(); } async init() { try { // 初始化WebGPU渲染器 this.renderer = new WebGPURenderer({ antialias: true, alpha: true, depth: true }); await this.renderer.init(); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x222222); this.container.appendChild(this.renderer.domElement); // 设置相机和控制器 this.camera.position.set(5, 5, 5); this.camera.lookAt(0, 0, 0); this.controls = new OrbitControls(this.camera, this.renderer.domElement); this.controls.enableDamping = true; // 场景设置(与WebGL版本相同) this.setupScene(); window.addEventListener('resize', this.onWindowResize.bind(this)); this.animate(); } catch (error) { console.error('WebGPU初始化失败,回退到WebGL:', error); this.fallbackToWebGL(); } } setupScene() { // 环境光 const ambientLight = new THREE.AmbientLight(0x404040); this.scene.add(ambientLight); // 方向光 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(10, 10, 5); this.scene.add(directionalLight); // 立方体 const geometry = new THREE.BoxGeometry(2, 2, 2); const material = new THREE.MeshStandardMaterial({ color: 0x00ff00, roughness: 0.5, metalness: 0.5 }); this.cube = new THREE.Mesh(geometry, material); this.scene.add(this.cube); // 地面 const floorGeometry = new THREE.PlaneGeometry(10, 10); const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.8, metalness: 0.2 }); this.floor = new THREE.Mesh(floorGeometry, floorMaterial); this.floor.rotation.x = -Math.PI / 2; this.scene.add(this.floor); } fallbackToWebGL() { this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x222222); this.container.innerHTML = ''; this.container.appendChild(this.renderer.domElement); console.log('已回退到WebGL渲染器'); } onWindowResize() { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); } animate() { requestAnimationFrame(this.animate.bind(this)); this.cube.rotation.x += 0.01; this.cube.rotation.y += 0.01; this.controls.update(); this.renderer.render(this.scene, this.camera); } }

3.3 渲染器性能对比

通过简单的性能测试对比WebGL和WebGPU在不同场景下的表现:

// 文件路径:src/utils/performance-test.js export class PerformanceTest { constructor() { this.results = {}; } async testWebGL(sceneComplexity = 100) { const startTime = performance.now(); // 模拟复杂场景渲染 for (let i = 0; i < sceneComplexity; i++) { // 复杂的渲染操作 await this.simulateRendering(); } const endTime = performance.now(); return endTime - startTime; } async testWebGPU(sceneComplexity = 100) { const startTime = performance.now(); // WebGPU通常有更好的并行处理能力 const promises = []; for (let i = 0; i < sceneComplexity; i++) { promises.push(this.simulateRendering()); } await Promise.all(promises); const endTime = performance.now(); return endTime - startTime; } async simulateRendering() { // 模拟渲染计算 return new Promise(resolve => { setTimeout(resolve, Math.random() * 10); }); } async runComparativeTest() { const complexities = [10, 50, 100, 500]; for (const complexity of complexities) { const webglTime = await this.testWebGL(complexity); const webgpuTime = await this.testWebGPU(complexity); this.results[complexity] = { webgl: webglTime, webgpu: webgpuTime, improvement: ((webglTime - webgpuTime) / webglTime * 100).toFixed(2) }; } return this.results; } }

4. 纹理与材质高级应用

4.1 UV坐标与纹理映射原理

UV坐标是2D纹理坐标,用于将2D图像映射到3D模型表面。每个顶点都有对应的UV坐标,告诉渲染器如何将纹理贴到模型上。

不规则平面的纹理映射示例:

// 文件路径:src/scenes/uv-mapping-demo.js import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; export class UVMappingDemo { constructor(container) { this.container = container; this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.init(); } async init() { // 设置渲染器 this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x222222); this.container.appendChild(this.renderer.domElement); // 相机和控制器 this.camera.position.set(0, 0, 10); this.controls = new OrbitControls(this.camera, this.renderer.domElement); // 创建不规则几何体 await this.createIrregularGeometry(); // 动画循环 this.animate(); } async createIrregularGeometry() { // 创建自定义几何体演示UV映射 const geometry = new THREE.BufferGeometry(); // 顶点位置 const vertices = new Float32Array([ -2, -2, 0, // 左下 2, -2, 0, // 右下 0, 2, 0, // 上中 -1, 0, 1, // 左中前 1, 0, 1 // 右中前 ]); // 面索引 const indices = [ 0, 1, 2, // 基础三角形 0, 3, 2, // 左侧面 1, 4, 2, // 右侧面 0, 1, 3, // 底面左 1, 4, 3 // 底面右 ]; // UV坐标 - 关键部分 const uvs = new Float32Array([ 0, 0, // 顶点0 - 左下 1, 0, // 顶点1 - 右下 0.5, 1, // 顶点2 - 上中 0, 0.5, // 顶点3 - 左中 1, 0.5 // 顶点4 - 右中 ]); geometry.setIndex(indices); geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2)); geometry.computeVertexNormals(); // 加载纹理 const textureLoader = new THREE.TextureLoader(); const texture = await new Promise((resolve) => { textureLoader.load('/textures/checkerboard.png', resolve); }); // 创建材质 const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide }); this.mesh = new THREE.Mesh(geometry, material); this.scene.add(this.mesh); // 添加辅助网格显示UV分布 this.addUVHelper(); } addUVHelper() { // UV可视化辅助 const helperGeometry = new THREE.PlaneGeometry(4, 4); const helperMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true, transparent: true, opacity: 0.3 }); this.helper = new THREE.Mesh(helperGeometry, helperMaterial); this.helper.position.z = -1; this.scene.add(this.helper); } animate() { requestAnimationFrame(this.animate.bind(this)); if (this.mesh) { this.mesh.rotation.y += 0.005; } this.controls.update(); this.renderer.render(this.scene, this.camera); } }

4.2 高级材质技术:水流效果

实现Three.js水流效果,解决property 'flowDirection'不存在的兼容性问题:

// 文件路径:src/scenes/water-effect-demo.js import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; export class WaterEffectDemo { constructor(container) { this.container = container; this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.init(); } async init() { this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x87CEEB); this.renderer.shadowMap.enabled = true; this.container.appendChild(this.renderer.domElement); this.camera.position.set(0, 5, 10); this.controls = new OrbitControls(this.camera, this.renderer.domElement); await this.createWaterEffect(); this.createEnvironment(); this.animate(); } async createWaterEffect() { // 创建水面几何体 const waterGeometry = new THREE.PlaneGeometry(10, 10, 128, 128); // 自定义着色器材质实现水流效果 const waterMaterial = new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, waterColor: { value: new THREE.Color(0x0077be) }, lightDirection: { value: new THREE.Vector3(0.5, 1, 0.5).normalize() } }, vertexShader: ` uniform float time; varying vec2 vUv; varying vec3 vPosition; void main() { vUv = uv; vPosition = position; // 正弦波模拟水面波动 float wave = sin(position.x * 2.0 + time) * 0.2 + sin(position.y * 3.0 + time * 1.5) * 0.15; vec3 newPosition = position; newPosition.z += wave; gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); } `, fragmentShader: ` uniform vec3 waterColor; uniform vec3 lightDirection; uniform float time; varying vec2 vUv; varying vec3 vPosition; void main() { // 基础颜色 vec3 color = waterColor; // 模拟光反射 vec3 normal = vec3(0, 0, 1); float diffuse = max(dot(normal, lightDirection), 0.2); // 添加波纹效果 float ripple = sin(vPosition.x * 10.0 + time * 2.0) * 0.1 + cos(vPosition.y * 8.0 + time * 1.7) * 0.1; // 深度变化 float depth = 1.0 - abs(vPosition.x) * 0.1; color = mix(color, color * 0.7, depth); // 最终颜色计算 color *= diffuse; color += ripple * 0.3; gl_FragColor = vec4(color, 0.8); } `, transparent: true, side: THREE.DoubleSide }); this.water = new THREE.Mesh(waterGeometry, waterMaterial); this.water.rotation.x = -Math.PI / 2; this.scene.add(this.water); } createEnvironment() { // 添加环境光 const ambientLight = new THREE.AmbientLight(0x404040, 0.4); this.scene.add(ambientLight); // 添加方向光 const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(10, 10, 5); directionalLight.castShadow = true; this.scene.add(directionalLight); // 添加周围地形 const groundGeometry = new THREE.PlaneGeometry(20, 20); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x3a7d3a }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.position.y = -1; this.scene.add(ground); } animate() { requestAnimationFrame(this.animate.bind(this)); // 更新时间uniform if (this.water && this.water.material.uniforms.time) { this.water.material.uniforms.time.value = performance.now() * 0.001; } this.controls.update(); this.renderer.render(this.scene, this.camera); } }

5. 三维地理信息系统集成

5.1 百度地图WebGL点聚合优化

实现高效的百度地图点聚合功能,优化大数据量下的渲染性能:

// 文件路径:src/map/baidu-map-clustering.js export class BaiduMapClustering { constructor(map, options = {}) { this.map = map; this.options = { gridSize: 60, // 网格大小(像素) maxZoom: 18, // 最大缩放级别 minZoom: 3, // 最小缩放级别 clusterRadius: 80, // 聚合半径 ...options }; this.markers = []; this.clusters = []; this.customOverlays = []; this.init(); } init() { // 监听地图事件进行重新聚合 this.map.addEventListener('zoomend', this.cluster.bind(this)); this.map.addEventListener('moveend', this.cluster.bind(this)); } addMarker(marker) { this.markers.push(marker); this.cluster(); } addMarkers(markers) { this.markers.push(...markers); this.cluster(); } cluster() { const zoom = this.map.getZoom(); // 清除现有聚合点 this.clearClusters(); if (zoom > this.options.maxZoom) { // 显示所有单个标记 this.showAllMarkers(); return; } // 执行点聚合算法 this.executeClustering(zoom); } executeClustering(zoom) { const clusters = []; const projectedMarkers = []; // 将经纬度转换为像素坐标 this.markers.forEach(marker => { const point = this.map.pointToOverlayPixel(marker.getPosition()); projectedMarkers.push({ marker: marker, point: point }); }); // 基于网格的聚合算法 const grid = {}; projectedMarkers.forEach(projected => { const gridX = Math.floor(projected.point.x / this.options.gridSize); const gridY = Math.floor(projected.point.y / this.options.gridSize); const gridKey = `${gridX}_${gridY}`; if (!grid[gridKey]) { grid[gridKey] = { markers: [], center: { x: 0, y: 0 }, bounds: { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity } }; } grid[gridKey].markers.push(projected.marker); grid[gridKey].center.x += projected.point.x; grid[gridKey].center.y += projected.point.y; // 更新边界 grid[gridKey].bounds.minX = Math.min(grid[gridKey].bounds.minX, projected.point.x); grid[gridKey].bounds.maxX = Math.max(grid[gridKey].bounds.maxX, projected.point.x); grid[gridKey].bounds.minY = Math.min(grid[gridKey].bounds.minY, projected.point.y); grid[gridKey].bounds.maxY = Math.max(grid[gridKey].bounds.maxY, projected.point.y); }); // 创建聚合点 Object.values(grid).forEach(cell => { if (cell.markers.length === 1) { // 单个点直接显示 cell.markers[0].setMap(this.map); } else { // 创建聚合点 this.createClusterPoint(cell, zoom); } }); } createClusterPoint(cell, zoom) { const centerX = cell.center.x / cell.markers.length; const centerY = cell.center.y / cell.markers.length; // 将像素坐标转换回经纬度 const centerPoint = new BMap.Pixel(centerX, centerY); const centerPosition = this.map.overlayPixelToPoint(centerPoint); // 创建自定义聚合覆盖物 const clusterOverlay = this.createCustomClusterOverlay(centerPosition, cell.markers.length); clusterOverlay.setMap(this.map); this.clusters.push({ overlay: clusterOverlay, markers: cell.markers }); } createCustomClusterOverlay(position, count) { // 使用WebGL创建高性能聚合点 const CustomClusterOverlay = function(position, count) { this._position = position; this._count = count; }; CustomClusterOverlay.prototype = new BMap.Overlay(); CustomClusterOverlay.prototype.initialize = function(map) { this._map = map; // 创建Canvas元素用于WebGL渲染 const canvas = document.createElement('canvas'); canvas.width = 60; canvas.height = 60; canvas.style.position = 'absolute'; // WebGL上下文 const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); if (gl) { this.renderWebGLCluster(gl, this._count); } else { this.render2DCluster(canvas, this._count); } map.getPanes().markerPane.appendChild(canvas); this._canvas = canvas; return canvas; }; CustomClusterOverlay.prototype.draw = function() { const map = this._map; const pixel = map.pointToOverlayPixel(this._position); this._canvas.style.left = (pixel.x - 30) + 'px'; this._canvas.style.top = (pixel.y - 30) + 'px'; }; return new CustomClusterOverlay(position, count); } renderWebGLCluster(gl, count) { // WebGL聚合点渲染实现 const vertexShaderSource = ` attribute vec2 aPosition; void main() { gl_Position = vec4(aPosition, 0.0, 1.0); gl_PointSize = 50.0; } `; const fragmentShaderSource = ` precision mediump float; uniform vec3 uColor; void main() { float dist = length(gl_PointCoord - vec2(0.5)); if (dist > 0.5) discard; gl_FragColor = vec4(uColor, 0.8); } `; // 编译着色器程序 const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexShaderSource); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); // 根据数量设置颜色 const colorUniform = gl.getUniformLocation(program, 'uColor'); let color; if (count < 10) color = [0.2, 0.8, 0.2]; else if (count < 50) color = [0.8, 0.8, 0.2]; else color = [0.8, 0.2, 0.2]; gl.uniform3fv(colorUniform, color); // 绘制点 gl.drawArrays(gl.POINTS, 0, 1); } clearClusters() { this.clusters.forEach(cluster => { cluster.overlay.setMap(null); cluster.markers.forEach(marker => marker.setMap(null)); }); this.clusters = []; } showAllMarkers() { this.markers.forEach(marker => marker.setMap(this.map)); } destroy() { this.clearClusters(); this.markers = []; } }

5.2 Three.js经纬度坐标回显

实现地理坐标与Three.js世界坐标的转换系统:

// 文件路径:src/utils/coordinate-converter.js export class CoordinateConverter { constructor(options = {}) { this.options = { earthRadius: 6371000, // 地球半径(米) scale: 0.00001, // 缩放比例 center: [116.4, 39.9], // 中心点经纬度 [lng, lat] ...options }; this.initProjection(); } initProjection() { // 简单的墨卡托投影转换 this.projection = { toWorld: (lng, lat, height = 0) => { const rad = Math.PI / 180; const lngRad = lng * rad; const latRad = lat * rad; // 墨卡托投影 const x = this.options.earthRadius * lngRad * this.options.scale; const y = this.options.earthRadius * Math.log(Math.tan(Math.PI/4 + latRad/2)) * this.options.scale; // 相对中心点的偏移 const [centerLng, centerLat] = this.options.center; const centerX = this.options.earthRadius * centerLng * rad * this.options.scale; const centerY = this.options.earthRadius * Math.log(Math.tan(Math.PI/4 + centerLat*rad/2)) * this.options.scale; return new THREE.Vector3( x - centerX, height, y - centerY ); }, toGeographic: (x, y, z) => { const rad = 180 / Math.PI; const [centerLng, centerLat] = this.options.center; const centerX = this.options.earthRadius * centerLng * Math.PI/180 * this.options.scale; const centerY = this.options.earthRadius * Math.log(Math.tan(Math.PI/4 + centerLat*Math.PI/180/2)) * this.options.scale; const worldX = x + centerX; const worldZ = z + centerY; const lng = (worldX / (this.options.earthRadius * this.options.scale)) * rad; const lat = (2 * Math.atan(Math.exp(worldZ / (this.options.earthRadius * this.options.scale))) - Math.PI/2) * rad; return { lng, lat, height: y }; } }; } // 批量转换坐标 convertCoordinates(coordinates) { return coordinates.map(coord => { if (Array.isArray(coord)) { return this.projection.toWorld(...coord); } return this.projection.toWorld(coord.lng, coord.lat, coord.height || 0); }); } // 创建地理参考场景 createGeoreferencedScene(container) { const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 100000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement); // 设置相机位置(基于中心点) const centerWorld = this.projection.toWorld(...this.options.center, 1000); camera.position.copy(centerWorld); camera.lookAt(0, 0, 0); return { scene, camera, renderer }; } // 添加地理标记 addGeographicMarker(scene, lng, lat, height = 0, color = 0xff000

相关新闻

  • 房地产宣传片制作全解析:从传统宣传到数字化营销革新
  • ChatGPT Work早期测试:工作场景AI助手部署与API集成指南
  • 【Linux网络·加餐】frp内网穿透

最新新闻

  • AI问答系统对比:Agent与RAG技术解析与应用
  • 新品发布|Tattu TA-BC无人机电池检测器上线,多通道充电状态尽在掌握
  • C++成员变量初始化:从C26495报错解析对象生命周期与内存安全
  • Qwen3.8-Max-Preview 2.4T参数开源:大模型部署与量化技术解析
  • 2026年ALM工具哪个好用?8款主流产品对比与选型指南
  • C++二叉树深度优先搜索(DFS)详解:从递归到迭代与实战应用

日新闻

  • 亨得利盐城维修点在哪里?手表维修保养地址指南**公示(2026年7月最新) - 亨得利官方
  • 提升.NET API安全性:Boxed.AspNetCore.Swagger认证授权最佳实践
  • 帝舵佛山**网点地址更新:2026年7月售后热线电话与服务客户指南 - 帝舵中国官方服务中心

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号