// 全局变量 let scene, camera, renderer, controls; let water, sun, sky; let windTurbineModels = []; // 改为数组存储多个风电机组 let clock = new THREE.Clock(); // 在全局变量部分添加 let pointerLockControls; let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let moveUp = false; let moveDown = false; let velocity = new THREE.Vector3(); let direction = new THREE.Vector3(); let prevTime = performance.now(); // 在全局变量部分添加 let autoTourActive = false; let tourStartTime = 0; let currentTurbineIndex = 0; let tourPhase = 0; // 0: 全景展示, 1: 拉近观察, 2: 环绕观察, 3: 拉远离开 let phaseStartTime = 0; let tourMode = 'cinematic'; // 电影级漫游模式 // 在全局变量部分添加 let shipModels = []; // 修改初始化函数中的相机设置 function init() { console.log('开始初始化Three.js场景'); // 创建场景 scene = new THREE.Scene(); console.log('场景创建完成'); //天空盒 scene.background = new THREE.CubeTextureLoader() .setPath('./assets/sky/') .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']); //环境光 const ambientLight = new THREE.AmbientLight(0x666666, 20); // 柔和的白光 scene.add(ambientLight); // 创建相机 - 调整位置以更好地展示浮标 camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 5000); // 调整相机位置,使其能够完整展示浮标并位于中心 const cameraRadius = 800; // 距离浮标的距离 const cameraHeight = 200; // 相机高度 const cameraRotationAngle = THREE.MathUtils.degToRad(0); // 正面视角 const cameraX = cameraRadius * Math.sin(cameraRotationAngle); const cameraZ = cameraRadius * Math.cos(cameraRotationAngle); camera.position.set(cameraX, cameraHeight, cameraZ); camera.lookAt(0, 0, 0); // 确保相机朝向场景中心 console.log('相机创建完成,位置:', camera.position.x, camera.position.y, camera.position.z); //创建渲染器 renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true, preserveDrawingBuffer: true, antialias: true, stencil: true, powerPreference: 'high-performance', physicalCorrectLights: true, localClippingEnabled: true, alpha: true, }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.setClearColor(0x87CEEB, 1); // 确保canvas添加到正确的容器 const container = document.getElementById('container'); if (container) { container.appendChild(renderer.domElement); console.log('渲染器canvas已添加到container'); } else { document.body.appendChild(renderer.domElement); console.log('渲染器canvas已添加到body'); } // 设置canvas样式确保可见 renderer.domElement.style.display = 'block'; renderer.domElement.style.position = 'absolute'; renderer.domElement.style.top = '0'; renderer.domElement.style.left = '0'; renderer.domElement.style.zIndex = '1'; console.log('渲染器创建完成,canvas尺寸:', renderer.domElement.width, 'x', renderer.domElement.height); // 初始化指针锁定控制器 if (typeof THREE.PointerLockControls !== 'undefined') { pointerLockControls = new THREE.PointerLockControls(camera, document.body); // 添加点击进入指针锁定的事件 document.addEventListener('click', function () { if (pointerLockControls) { pointerLockControls.lock(); } }); // 添加键盘控制事件 document.addEventListener('keydown', onKeyDown, false); document.addEventListener('keyup', onKeyUp, false); console.log('指针锁定控制器创建完成'); } else { console.warn('PointerLockControls未加载,使用OrbitControls'); // 保留原有的OrbitControls作为备选 initOrbitControls(); } // 添加相机控制 (OrbitControls 作为备选) function initOrbitControls() { if (typeof THREE.OrbitControls !== 'undefined') { controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; controls.screenSpacePanning = false; controls.minDistance = 300; // 增加最小距离,确保能看到整个浮标 controls.maxDistance = 2000; // 最大距离 controls.maxPolarAngle = Math.PI / 2 - 0.1; // 限制垂直旋转角度,防止看到水面以下 controls.target.set(0, 0, 0); controls.addEventListener('change', function () { // 限制相机高度,防止进入水面以下 if (camera.position.y < 10) { camera.position.y = 10; } }); console.log('相机控制器创建完成'); } else { console.warn('OrbitControls未加载,跳过相机控制器'); } } addLights(); createOcean(); // 创建海洋 loadBuoys(); // 加载浮标模型 // 监听窗口大小变化 window.addEventListener('resize', onWindowResize, false); console.log('Three.js场景初始化完成'); } // 加载HDR环境纹理 function loadHDRTexture() { // 检查RGBELoader是否可用 if (typeof THREE.RGBELoader === 'undefined') { console.warn('RGBELoader未加载,跳过HDR纹理加载'); return; } const hdrLoader = new THREE.RGBELoader(); hdrLoader.load("./textures/023.hdr", (texture) => { // 设置背景和环境纹理 texture.mapping = THREE.EquirectangularReflectionMapping; scene.background = texture; scene.environment = texture; console.log('HDR纹理加载完成'); }, undefined, (error) => { console.error('HDR纹理加载失败:', error); }); } // 修改后的 createSkybox 函数:// 创建天空盒 function createSkybox() { const skyGeometry = new THREE.SphereGeometry(1800, 24, 18); // 增大天空盒半径 const textureLoader = new THREE.TextureLoader().load('./textures/sky.jpg'); textureLoader.colorSpace = THREE.SRGBColorSpace; // 创建天空材质(通常使用全景纹理) const skyMaterial = new THREE.MeshBasicMaterial({ map: textureLoader, side: THREE.BackSide // 重要:让材质渲染在球体内侧 }); // 创建天空网格 const skySphere = new THREE.Mesh(skyGeometry, skyMaterial); scene.add(skySphere); } // 创建天空效果 function createSky() { // 注释掉Sky.js的高级天空效果 sky = new THREE.Sky(); sky.scale.setScalar(450000); scene.add(sky); // 创建太阳 sun = new THREE.Vector3(); // 配置天空参数(正午阳光设置) const skyUniforms = sky.material.uniforms; skyUniforms['turbidity'].value = 2; // 降低湍流度,营造清澈的正午天空 skyUniforms['rayleigh'].value = 3; // 增强瑞利散射,让天空更蓝 skyUniforms['mieCoefficient'].value = 0.005; skyUniforms['mieDirectionalG'].value = 0.8; // 设置太阳位置(正午阳光效果) const phi = THREE.MathUtils.degToRad(90 - 85); // 仰角 - 太阳接近天顶 const theta = THREE.MathUtils.degToRad(0); // 方位角 - 正南方向 sun.setFromSphericalCoords(1, phi, theta); skyUniforms['sunPosition'].value.copy(sun); // 使用sky.jpg作为天空盒纹理 const textureLoader = new THREE.TextureLoader(); textureLoader.load('./textures/sky.jpg', function (texture) { // 创建天空盒几何体 const skyBoxGeometry = new THREE.SphereGeometry(2000, 32, 32); const skyBoxMaterial = new THREE.MeshBasicMaterial({ map: texture, side: THREE.BackSide }); const skyBox = new THREE.Mesh(skyBoxGeometry, skyBoxMaterial); scene.add(skyBox); console.log('Sky texture loaded as skybox'); }, undefined, function (error) { console.error('Sky texture loading failed:', error); // 创建备用天空颜色 const skyGeometry = new THREE.SphereGeometry(500, 32, 32); const skyMaterial = new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide }); const sky = new THREE.Mesh(skyGeometry, skyMaterial); scene.add(sky); console.log('Fallback sky color created'); }); console.log('Sky created using sky.jpg texture'); } // 创建海洋效果 function createOcean() { // 加载水面法线纹理 const textureLoader = new THREE.TextureLoader(); const waterNormals = textureLoader.load('./textures/waternormals.jpg', function (texture) { // 设置法线纹理的重复模式,增强水面效果 texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(2, 2); // 调整重复次数以控制波浪密度 console.log('水面法线纹理加载成功'); }, undefined, function (error) { console.error('水面法线纹理加载失败:', error); }); // 设置法线纹理的包装模式(即使纹理加载失败也要设置默认值) waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping; waterNormals.repeat.set(2, 2); // 增加重复次数以获得更细腻的波浪效果 // 创建水面几何体,增大尺寸以匹配天空盒尺寸 const waterGeometry = new THREE.PlaneGeometry(10600, 10600); // 比天空盒稍大以确保完全覆盖 // 使用Water.js创建高级水面效果 // 其他碧绿色选项: // waterColor: 0x0077be, // 经典碧绿色 // waterColor: 0x00a86b, // 绿松石色 // waterColor: 0x2a52be, // 地中海蓝 // waterColor: 0x008080, // 水鸭色 // waterColor: 0x40e0d0, // 青绿色 water = new THREE.Water(waterGeometry, { textureWidth: 512, textureHeight: 512, waterNormals: waterNormals, sunDirection: new THREE.Vector3(0.70707, 0.70707, 0.0), sunColor: 0xffffff, waterColor: 0x0077be,//0x001e0f 0x40e0d0 distortionScale: 3.7, // 控制波浪的扭曲程度 fog: scene.fog !== undefined, alpha: 0.8, // 水面透明度 foam: 0.1 // 泡沫效果 }); water.rotation.x = -Math.PI / 2; water.position.y = 0; // 确保水面在y=0位置 scene.add(water); // 将water赋值给全局ocean变量以保持兼容性 ocean = water; console.log('高级水面效果创建完成,使用法线纹理增强波浪凹凸效果'); } // 修改 addLights 函数,增强光照效果// 修改 addLights 函数,进一步增强光照效果 function addLights() { // 大幅增强环境光强度 const ambientLight = new THREE.AmbientLight(0xffffff, 1.2); // 使用白色光,强度更高 scene.add(ambientLight); // 主方向光(太阳光)- 进一步调整位置和强度 const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5); // 增加强度到1.5 directionalLight.position.set(100, 150, 100); // 调整到更合理的位置 directionalLight.castShadow = true; directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; directionalLight.shadow.camera.near = 0.5; directionalLight.shadow.camera.far = 2000; // 增加远裁剪面 directionalLight.shadow.camera.left = -800; directionalLight.shadow.camera.right = 800; directionalLight.shadow.camera.top = 800; directionalLight.shadow.camera.bottom = -800; scene.add(directionalLight); // 添加更强的补光 - 减少阴影 const fillLight = new THREE.DirectionalLight(0xffffff, 0.8); // 增加补光强度到0.8 fillLight.position.set(-100, 100, -100); scene.add(fillLight); // 添加天光 - 模拟天空反射 const hemiLight = new THREE.HemisphereLight(0x80deea, 0x4caf50, 0.5); // 增加强度到0.5 hemiLight.position.set(0, 200, 0); scene.add(hemiLight); // 添加背面光,确保模型背面也有光照 const backLight = new THREE.DirectionalLight(0xffffff, 0.6); backLight.position.set(0, 50, -200); scene.add(backLight); } // 修改浮标加载函数,确保浮标更深地嵌入水下并增加调试信息 function loadBuoys() { console.log('开始加载科考浮标模型'); // 确保已引入 GLTFLoader if (typeof THREE.GLTFLoader === 'undefined') { console.warn('GLTFLoader未定义,请确认已正确引入GLTFLoader'); return; } const loader = new THREE.GLTFLoader(); // 只加载一个浮标,放置在场景中心 const position = { x: 250, z: 100 }; let loadedCount = 0; loader.load( './models/科考船3.glb', function (gltf) { console.log('科考浮标模型加载成功'); const object = gltf.scene; // 调整模型缩放和位置 - 根据GLB模型特点调整 object.scale.set(10, 10, 10); // 保持合适的缩放比例 // 将浮标位置设置在水下(y为负值表示在水面下方) object.position.set(position.x,-120, position.z); // 调整模型材质 object.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; child.frustumCulled = false; // 如果材质存在,调整其属性 if (child.material) { // 增强材质亮度和自发光效果 child.material.emissive = new THREE.Color(0x555555); // 显著增加自发光强度 child.material.emissiveIntensity = 0.5; // 增加自发光强度 // 调整材质颜色,使其更亮 if (child.material.color) { // 提亮原始颜色 child.material.color.multiplyScalar(1.5); } // 确保材质响应光照 if (child.material.type === 'MeshBasicMaterial') { // 如果是基础材质,替换为标准材质 child.material = new THREE.MeshStandardMaterial({ color: child.material.color || 0xffffff, map: child.material.map, metalness: 0.2, roughness: 0.5, emissive: new THREE.Color(0x444444), // 添加自发光 emissiveIntensity: 0.4 }); } else if (child.material.type === 'MeshStandardMaterial') { // 调整标准材质参数使其更亮 child.material.metalness = Math.max(0.1, child.material.metalness || 0.2); child.material.roughness = Math.min(0.7, child.material.roughness || 0.5); child.material.emissive = new THREE.Color(0x444444); child.material.emissiveIntensity = 0.4; // 增加自发光强度 } else if (child.material.type === 'MeshLambertMaterial') { // 对于Lambert材质也添加自发光 child.material.emissive = new THREE.Color(0x444444); child.material.emissiveIntensity = 0.4; } // 确保材质更新 child.material.needsUpdate = true; } } }); // 添加到场景和数组 scene.add(object); shipModels.push(object); loadedCount++; console.log('科考浮标模型加载完成!'); }, function (progress) { if (progress.total > 0) { const percent = (progress.loaded / progress.total * 100).toFixed(1); console.log(`科考浮标模型加载进度: ${percent}%`); } }, function (error) { console.error('科考浮标模型加载失败:', error); loadedCount++; } ); } // 修改船只动画函数,调整船只在水面上的位置 function animate() { requestAnimationFrame(animate); const time = clock.getElapsedTime(); // 更新自动漫游(如果激活) updateAutoTour(); // 原有的键盘控制逻辑(当自动漫游未激活时) if (!autoTourActive) { const delta = Math.min(0.1, (time - prevTime)); // 如果使用指针锁定控制器且已锁定 if (pointerLockControls && pointerLockControls.isLocked) { // 计算移动方向 direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.y = Number(moveUp) - Number(moveDown); direction.normalize(); // 设置移动速度 const speed = 100.0; // 根据方向和速度更新速度向量 if (moveForward || moveBackward) velocity.z -= direction.z * speed * delta; if (moveLeft || moveRight) velocity.x -= direction.x * speed * delta; if (moveUp || moveDown) velocity.y -= direction.y * speed * delta; // 应用阻尼(摩擦力) velocity.x -= velocity.x * 10.0 * delta; velocity.y -= velocity.y * 10.0 * delta; velocity.z -= velocity.z * 10.0 * delta; // 移动相机 pointerLockControls.moveRight(-velocity.x * delta); pointerLockControls.moveForward(-velocity.z * delta); // 垂直移动 camera.position.y -= velocity.y * delta; // 简单的高度限制 if (camera.position.y < 5) { camera.position.y = 5; velocity.y = 0; } if (camera.position.y > 500) { camera.position.y = 500; velocity.y = 0; } } // 如果使用OrbitControls else if (controls) { controls.update(); } prevTime = time; } // 在前几秒强制更新材质(解决材质加载延迟问题) if (time < 3 && shipModels.length > 0) { shipModels.forEach(model => { model.traverse(function (child) { if (child.isMesh && child.material) { if (child.material.needsUpdate !== undefined) { child.material.needsUpdate = true; } // 持续调整材质亮度 if (child.material.emissive) { child.material.emissiveIntensity = 0.5; } } }); }); } // 更新水面动画 - 增强水面波动效果 if (water && water.material.uniforms) { // 通过改变time值来控制水面波动动画 water.material.uniforms['time'].value += 1.0 / 60.0; } // 船只动画(如果有需要的话) if (shipModels.length > 0) { // shipModels.forEach((model) => { // // 可以为船只添加浮动动画效果 // const time = clock.getElapsedTime(); // // 轻微的上下浮动效果 // model.position.y = Math.sin(time * 0.5) * 0.5; // // 轻微的摇摆效果 // model.rotation.z = Math.sin(time * 0.3) * 0.02; // model.rotation.x = Math.cos(time * 0.4) * 0.015; // }); } // 渲染场景 if (scene && camera && renderer) { renderer.render(scene, camera); // 每隔一段时间输出渲染信息 if (Math.floor(time) % 5 === 0 && time % 1 < 0.1) { console.log('正在渲染场景,时间:', Math.floor(time), '场景对象数量:', scene.children.length); } } else { console.warn('场景、相机或渲染器未初始化'); } } // 窗口大小变化处理 function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // 页面加载完成后初始化 window.addEventListener('DOMContentLoaded', function () { console.log('DOM加载完成,开始初始化Three.js场景'); // 检查Three.js是否加载 if (typeof THREE === 'undefined') { console.error('Three.js未加载!'); document.getElementById('loading').innerHTML = '错误:Three.js库未加载'; return; } console.log('Three.js版本:', THREE.REVISION); // 隐藏加载提示 const loadingElement = document.getElementById('loading'); if (loadingElement) { loadingElement.style.display = 'none'; } init(); animate(); }); // 添加切换自动漫游的函数 function toggleAutoTour() { autoTourActive = !autoTourActive; if (autoTourActive) { tourStartTime = clock.getElapsedTime(); phaseStartTime = tourStartTime; currentTurbineIndex = 0; tourPhase = 0; console.log('自动漫游已启动'); } else { console.log('自动漫游已停止'); } // 更新按钮文本 const button = document.getElementById('tourButton'); if (button) { button.textContent = autoTourActive ? '停止漫游' : '开始漫游'; } return autoTourActive; } // 获取当前风机位置 function getCurrentTurbinePosition() { if (windTurbineModels.length > 0) { // 如果有多个风机,可以循环访问 const turbine = windTurbineModels[currentTurbineIndex % windTurbineModels.length]; return turbine.position.clone(); } // 默认位置 return new THREE.Vector3(0, 0, 0); } // 计算风机群的中心位置和边界 function getWindFarmBounds() { if (windTurbineModels.length === 0) { return { center: new THREE.Vector3(0, 0, 0), radius: 100 }; } const positions = windTurbineModels.map(model => model.position); let minX = Infinity, maxX = -Infinity; let minZ = Infinity, maxZ = -Infinity; positions.forEach(pos => { minX = Math.min(minX, pos.x); maxX = Math.max(maxX, pos.x); minZ = Math.min(minZ, pos.z); maxZ = Math.max(maxZ, pos.z); }); const center = new THREE.Vector3( (minX + maxX) / 2, 0, (minZ + maxZ) / 2 ); const radius = Math.max( Math.abs(maxX - minX) / 2, Math.abs(maxZ - minZ) / 2 ) * 1.5; return { center, radius }; } // 电影级自动漫游逻辑 function updateCinematicTour() { if (!autoTourActive || windTurbineModels.length === 0) return; const elapsedTime = clock.getElapsedTime() - tourStartTime; const phaseElapsed = clock.getElapsedTime() - phaseStartTime; // 每个阶段持续时间(秒) const phaseDuration = 10; // 增加到10秒,给更多时间展示 // 检查是否需要切换到下一个阶段 if (phaseElapsed > phaseDuration) { tourPhase = (tourPhase + 1) % 4; phaseStartTime = clock.getElapsedTime(); console.log(`切换到阶段 ${tourPhase}`); // 如果完成一轮,切换到下一个风机 if (tourPhase === 0 && windTurbineModels.length > 1) { currentTurbineIndex = (currentTurbineIndex + 1) % windTurbineModels.length; console.log(`切换到风机 ${currentTurbineIndex + 1}`); } } const currentPhaseElapsed = clock.getElapsedTime() - phaseStartTime; const phaseProgress = Math.min(currentPhaseElapsed / phaseDuration, 1); // 获取当前风机位置 const turbinePosition = getCurrentTurbinePosition(); // 根据阶段设置相机位置和目标 let cameraPosition, targetPosition; switch (tourPhase) { case 0: // 全景展示阶段 - 从高空俯视整个风机群,然后慢慢下降推进 const bounds = getWindFarmBounds(); // 初始位置在高空俯视整个风机群 const initialHeight = bounds.radius; const finalHeight = bounds.radius * 0.3; const overviewDistance = bounds.radius * 0.8; // 相机从高空慢慢下降 const currentHeight = initialHeight + (finalHeight - initialHeight) * phaseProgress; // 相机围绕风机群中心旋转并下降 const overviewAngle = elapsedTime * 0.2; cameraPosition = new THREE.Vector3( bounds.center.x + Math.cos(overviewAngle) * overviewDistance, currentHeight, bounds.center.z + Math.sin(overviewAngle) * overviewDistance ); targetPosition = bounds.center.clone(); targetPosition.y = 30; break; case 1: // 拉近阶段 - 从远景慢慢拉近到当前风机叶片 // 初始距离和高度 const startDistance = 150; const endDistance = 40; // 更近的距离 const startHeight = 40; const endHeight = 35; // 更低的高度,更接近叶片 // 插值计算当前距离和高度 const currentDistance = startDistance + (endDistance - startDistance) * phaseProgress; const currentHeightVal = startHeight + (endHeight - startHeight) * phaseProgress; // 相机从侧面接近风机,逐渐移动到正面 const approachAngle = Math.PI / 2; // 从侧面到正面 cameraPosition = new THREE.Vector3( turbinePosition.x + Math.cos(approachAngle) * currentDistance, turbinePosition.y + currentHeightVal, turbinePosition.z + Math.sin(approachAngle) * currentDistance ); // 目标点逐渐从塔筒上部移动到叶片区域 const targetHeight = 35 + Math.sin(phaseProgress * Math.PI) * 10; targetPosition = new THREE.Vector3( turbinePosition.x, turbinePosition.y + targetHeight, turbinePosition.z ); break; case 2: // 叶片观察阶段 - 围绕风机叶片观察 // 围绕风机叶片的路径,重点观察叶片 const bladeOrbitRadius = 30; // 更小的半径,更接近叶片 const bladeOrbitHeight = 30 + Math.sin(phaseProgress * Math.PI * 2) * 15; // 在叶片高度附近上下移动 // 围绕风机做椭圆形运动,重点观察叶片 const bladeAngle = elapsedTime * 0.6; cameraPosition = new THREE.Vector3( turbinePosition.x + Math.cos(bladeAngle) * bladeOrbitRadius * 1.5, // 椭圆轨道 turbinePosition.y + bladeOrbitHeight, turbinePosition.z + Math.sin(bladeAngle) * bladeOrbitRadius ); // 目标点跟踪叶片运动 targetPosition = new THREE.Vector3( turbinePosition.x + Math.cos(bladeAngle * 2) * 10, turbinePosition.y + 35 + Math.sin(phaseProgress * Math.PI * 3) * 5, turbinePosition.z + Math.sin(bladeAngle * 2) * 10 ); break; case 3: // 拉远阶段 - 从风机拉远并准备切换到下一个 // 从当前接近位置开始拉远 const pullbackStartDistance = 40; const pullbackEndDistance = 150; const pullbackStartHeight = 35; const pullbackEndHeight = 70; // 插值计算当前距离和高度 const pullbackDistance = pullbackStartDistance + (pullbackEndDistance - pullbackStartDistance) * phaseProgress; const pullbackHeight = pullbackStartHeight + (pullbackEndHeight - pullbackStartHeight) * phaseProgress; // 相机逐渐拉远并升高,同时移动到下一个风机的方向 const pullbackAngle = Math.PI / 4 * phaseProgress; // 逐渐改变角度 cameraPosition = new THREE.Vector3( turbinePosition.x + Math.cos(pullbackAngle) * pullbackDistance, turbinePosition.y + pullbackHeight, turbinePosition.z + Math.sin(pullbackAngle) * pullbackDistance ); // 目标点逐渐移向下一个风机的方向 targetPosition = new THREE.Vector3( turbinePosition.x, turbinePosition.y + 30, turbinePosition.z ); break; } // 设置相机位置和朝向 camera.position.copy(cameraPosition); camera.lookAt(targetPosition); } // 自动漫游逻辑(保持原有函数名以兼容调用) function updateAutoTour() { updateCinematicTour(); } // 添加键盘快捷键来切换自动漫游 function onKeyDown(event) { // 原有的键盘控制代码... switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; break; case 'KeyQ': moveUp = true; break; case 'KeyE': moveDown = true; break; case 'KeyT': // 按 T 键切换自动漫游 const isActive = toggleAutoTour(); console.log(`自动漫游 ${isActive ? '启动' : '停止'}`); break; } } function onKeyUp(event) { // 原有的键盘释放代码... switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; break; case 'ArrowDown': case 'KeyS': moveBackward = false; break; case 'ArrowRight': case 'KeyD': moveRight = false; break; case 'KeyQ': moveUp = false; break; case 'KeyE': moveDown = false; break; } }