From e7d4267f6018d3721e5e2a5d39e1b3c2b72eb9dc Mon Sep 17 00:00:00 2001 From: como Date: Thu, 26 Mar 2026 14:06:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 13 ------- src/OceanScene.js | 31 ++++++++-------- src/main.js | 90 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/index.html b/index.html index 9524e77..5a7368a 100644 --- a/index.html +++ b/index.html @@ -60,17 +60,6 @@ to { transform: rotate(360deg); } } - #stats { - position: fixed; - bottom: 20px; - left: 20px; - background: rgba(0, 0, 0, 0.7); - padding: 10px 15px; - border-radius: 8px; - color: white; - font-size: 12px; - z-index: 100; - } @@ -82,8 +71,6 @@
-
FPS: 60
- diff --git a/src/OceanScene.js b/src/OceanScene.js index f1f0379..b0c4fd9 100644 --- a/src/OceanScene.js +++ b/src/OceanScene.js @@ -6,6 +6,7 @@ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; +import Stats from 'three/addons/libs/stats.module.js'; import { TerrainGenerator } from './TerrainGenerator.js'; import { VegetationSystem } from './VegetationSystem.js'; @@ -35,6 +36,7 @@ export class OceanScene { this.bloomPass = null; this.rainPass = null; this.snowPass = null; + this.stats = null; this.cloudGroup = null; this.cloudMaterials = []; this.cloudLayers = []; @@ -83,8 +85,6 @@ export class OceanScene { }; this.clock = new THREE.Clock(); - this.frameCount = 0; - this.lastTime = performance.now(); this.lightningFlash = 0; this.lightningLocalFlash = 0; this.lightningBurstEnd = 0; @@ -94,6 +94,7 @@ export class OceanScene { async init() { this.initRenderer(); + this.initStats(); this.initScene(); this.initCamera(); this.initControls(); @@ -123,6 +124,18 @@ export class OceanScene { this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; this.container.appendChild(this.renderer.domElement); } + + initStats() { + this.stats = new Stats(); + this.stats.showPanel(0); + this.stats.dom.style.position = 'fixed'; + this.stats.dom.style.left = '0'; + this.stats.dom.style.top = '0'; + this.stats.dom.style.bottom = 'auto'; + this.stats.dom.style.margin = '0'; + this.stats.dom.style.zIndex = '120'; + this.container.appendChild(this.stats.dom); + } initScene() { this.scene = new THREE.Scene(); @@ -1535,6 +1548,7 @@ export class OceanScene { animate() { requestAnimationFrame(() => this.animate()); + this.stats?.begin(); const time = this.clock.getElapsedTime(); this.updateLightning(time); @@ -1589,18 +1603,7 @@ export class OceanScene { } else { this.renderer.render(this.scene, this.camera); } - - this.frameCount++; - const currentTime = performance.now(); - if (currentTime - this.lastTime >= 1000) { - const fps = Math.round(this.frameCount * 1000 / (currentTime - this.lastTime)); - const fpsElement = document.getElementById('fps'); - if (fpsElement) { - fpsElement.textContent = fps; - } - this.frameCount = 0; - this.lastTime = currentTime; - } + this.stats?.end(); } hideLoading() { diff --git a/src/main.js b/src/main.js index 85864d2..743469c 100644 --- a/src/main.js +++ b/src/main.js @@ -37,6 +37,89 @@ function setupControls(oceanScene) { gui.domElement.style.zIndex = '120'; const params = oceanScene.params; + const presetKeys = [ + 'elevation', + 'azimuth', + 'exposure', + 'turbidity', + 'rayleigh', + 'bloomStrength', + 'bloomRadius', + 'cloudCoverage', + 'cloudDensity', + 'cloudElevation', + 'fogDensity', + 'fogHeight', + 'fogRange', + 'rainEnabled', + 'rainScreenIntensity', + 'rainVeilIntensity', + 'rainDropSize', + 'rainSpeed', + 'rainAudioEnabled', + 'rainAudioVolume', + 'snowEnabled', + 'snowIntensity', + 'snowSpeed', + 'lightningEnabled', + 'lightningIntensity' + ]; + const presetComments = { + elevation: '太阳高度角', + azimuth: '太阳方位角', + exposure: '场景曝光度', + turbidity: '天空浑浊度', + rayleigh: '瑞利散射强度', + bloomStrength: '泛光强度', + bloomRadius: '泛光扩散范围', + cloudCoverage: '云层覆盖度', + cloudDensity: '云层密度', + cloudElevation: '云层高度', + fogDensity: '雾气浓度', + fogHeight: '雾气高度', + fogRange: '雾气范围', + rainEnabled: '是否启用雨效', + rainScreenIntensity: '屏幕雨滴强度', + rainVeilIntensity: '雨线强度', + rainDropSize: '雨滴尺寸', + rainSpeed: '雨效速度', + rainAudioEnabled: '是否启用雨声', + rainAudioVolume: '雨声音量', + snowEnabled: '是否启用降雪', + snowIntensity: '雪量', + snowSpeed: '降雪速度', + lightningEnabled: '是否启用雷闪', + lightningIntensity: '雷闪强度' + }; + + const exportActions = { + 导出预设: () => { + const preset = { + meta: { + version: 1, + comment: '场景预设导出文件', + exportedAt: new Date().toISOString() + }, + comments: Object.fromEntries( + presetKeys.map((key) => [key, presetComments[key]]) + ), + params: Object.fromEntries( + presetKeys.map((key) => [key, params[key]]) + ) + }; + + const blob = new Blob([JSON.stringify(preset, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + const stamp = new Date().toISOString().replace(/[:.]/g, '-'); + link.href = url; + link.download = `scene-preset-${stamp}.json`; + link.click(); + URL.revokeObjectURL(url); + } + }; + + gui.add(exportActions, '导出预设'); const skyFolder = gui.addFolder('天空'); skyFolder.add(params, 'elevation', 0, 90, 0.1).name('太阳高度').onChange((value) => oceanScene.setSunElevation(value)); @@ -46,8 +129,8 @@ function setupControls(oceanScene) { skyFolder.add(params, 'rayleigh', 0, 4, 0.01).name('瑞利散射').onChange((value) => oceanScene.setRayleigh(value)); const bloomFolder = gui.addFolder('泛光'); - bloomFolder.add(params, 'bloomStrength', 0, 3, 0.01).name('强度').onChange((value) => oceanScene.setBloomStrength(value)); - bloomFolder.add(params, 'bloomRadius', 0, 1, 0.01).name('扩散').onChange((value) => oceanScene.setBloomRadius(value)); + bloomFolder.add(params, 'bloomStrength', 0, 1, 0.01).name('强度').onChange((value) => oceanScene.setBloomStrength(value)); + bloomFolder.add(params, 'bloomRadius', 0, 3, 0.01).name('扩散').onChange((value) => oceanScene.setBloomRadius(value)); const cloudFolder = gui.addFolder('云层'); cloudFolder.add(params, 'cloudCoverage', 0, 1, 0.01).name('覆盖度').onChange((value) => oceanScene.setCloudCoverage(value)); @@ -75,8 +158,7 @@ function setupControls(oceanScene) { snowFolder.add(params, 'snowIntensity', 0, 1.5, 0.01).name('雪量').onChange((value) => oceanScene.setSnowIntensity(value)); snowFolder.add(params, 'snowSpeed', 0.2, 2.2, 0.01).name('速度').onChange((value) => oceanScene.setSnowSpeed(value)); - skyFolder.open(); - rainFolder.open(); + gui.close(); } main().catch(console.error);