You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

833 lines
30 KiB
JavaScript

// 全局变量
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;
}
}