diff --git a/index.html b/index.html index db58eb6..9524e77 100644 --- a/index.html +++ b/index.html @@ -60,131 +60,6 @@ to { transform: rotate(360deg); } } - #info { - position: fixed; - top: 20px; - left: 20px; - color: white; - background: rgba(0, 0, 0, 0.6); - padding: 15px 20px; - border-radius: 10px; - font-size: 14px; - z-index: 100; - backdrop-filter: blur(10px); - max-width: 300px; - } - - #info h2 { - margin-bottom: 10px; - font-size: 16px; - } - - #info p { - margin: 5px 0; - opacity: 0.9; - } - - #sun-controls { - position: fixed; - top: 20px; - right: 20px; - background: rgba(0, 0, 0, 0.7); - padding: 15px; - border-radius: 10px; - color: white; - z-index: 100; - backdrop-filter: blur(10px); - min-width: 280px; - max-height: calc(100vh - 40px); - overflow-y: auto; - } - - #sun-controls h3 { - margin-bottom: 12px; - font-size: 14px; - border-bottom: 1px solid rgba(255,255,255,0.2); - padding-bottom: 8px; - } - - .control-section { - margin-bottom: 10px; - } - - .control-section-title { - font-size: 11px; - opacity: 0.6; - margin-bottom: 6px; - text-transform: uppercase; - letter-spacing: 0.5px; - } - - .control-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px 12px; - } - - .control-group { - margin-bottom: 0; - } - - .control-group.full-width { - grid-column: 1 / -1; - } - - .control-group label { - display: block; - margin-bottom: 3px; - font-size: 11px; - opacity: 0.85; - } - - .control-group input[type="range"] { - width: 100%; - height: 4px; - border-radius: 2px; - background: rgba(255,255,255,0.2); - outline: none; - -webkit-appearance: none; - } - - .control-group input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - width: 12px; - height: 12px; - border-radius: 50%; - background: #4a9eff; - cursor: pointer; - } - - .control-toggle { - display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 10px; - border-radius: 8px; - background: rgba(255,255,255,0.06); - margin-bottom: 8px; - } - - .control-toggle label { - font-size: 12px; - opacity: 0.9; - } - - .control-toggle input[type="checkbox"] { - width: 16px; - height: 16px; - accent-color: #5da9ff; - } - - .control-value { - text-align: right; - font-size: 10px; - opacity: 0.6; - margin-top: 1px; - } - #stats { position: fixed; bottom: 20px; @@ -207,183 +82,6 @@
-
-

🌊 写实无尽海洋

-

🖱️ 左键拖动旋转视角

-

🖱️ 右键拖动平移

-

🖱️ 滚轮缩放

-

☀️ 使用右上角控制太阳

-
- -
-

☀️ 场景控制

- -
-
太阳 & 光照
-
-
- - -
2.0°
-
-
- - -
180.0°
-
-
- - -
0.10
-
-
- - -
10.0
-
-
- - -
2.00
-
-
-
- -
-
Bloom 效果
-
-
- - -
0.10
-
-
- - -
0.00
-
-
-
- -
-
云层
-
-
- - -
0.40
-
-
- - -
0.50
-
-
- - -
0.50
-
-
-
- -
-
雾气
-
-
- - -
0.42
-
-
- - -
0.32
-
-
- - -
0.55
-
-
-
- -
-
雨效
-
- - -
-
-
- - -
0.41
-
-
- - -
1.15
-
-
- - -
1.00
-
-
- - -
1.00
-
-
-
- - -
-
-
- - -
0.35
-
-
-
- -
-
雪效
-
- - -
-
-
- - -
0.65
-
-
- - -
0.85
-
-
-
- -
-
雷闪
-
- - -
-
-
- - -
0.75
-
-
-
-
-
FPS: 60
diff --git a/src/OceanScene.js b/src/OceanScene.js index 6541fc5..f1f0379 100644 --- a/src/OceanScene.js +++ b/src/OceanScene.js @@ -1355,7 +1355,7 @@ export class OceanScene { 1 ); const delay = THREE.MathUtils.lerp(0.65, 2.4, distanceNorm) + Math.random() * 0.45; - const volume = this.params.lightningIntensity * THREE.MathUtils.lerp(0.9, 0.42, distanceNorm); + const volume = this.params.lightningIntensity * THREE.MathUtils.lerp(1.0, 0.58, distanceNorm) * 1.12; this.scheduledThunder.push({ playAt: this.lightningBurstEnd + delay, volume, diff --git a/src/main.js b/src/main.js index efc94c0..85864d2 100644 --- a/src/main.js +++ b/src/main.js @@ -1,3 +1,4 @@ +import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; import { OceanScene } from './OceanScene.js'; async function main() { @@ -30,52 +31,52 @@ async function main() { } function setupControls(oceanScene) { - const bindSlider = (id, formatter, setter) => { - const slider = document.getElementById(id); - const valueLabel = document.getElementById(`${id}-value`); - if (!slider || !valueLabel) return; + const gui = new GUI({ title: '场景控制' }); + gui.domElement.style.marginTop = '12px'; + gui.domElement.style.marginRight = '12px'; + gui.domElement.style.zIndex = '120'; - slider.addEventListener('input', (e) => { - const value = parseFloat(e.target.value); - setter(value); - valueLabel.textContent = formatter(value); - }); - }; + const params = oceanScene.params; - const bindCheckbox = (id, setter) => { - const checkbox = document.getElementById(id); - if (!checkbox) return; + const skyFolder = gui.addFolder('天空'); + skyFolder.add(params, 'elevation', 0, 90, 0.1).name('太阳高度').onChange((value) => oceanScene.setSunElevation(value)); + skyFolder.add(params, 'azimuth', -180, 180, 0.1).name('太阳方位').onChange((value) => oceanScene.setSunAzimuth(value)); + skyFolder.add(params, 'exposure', 0, 1, 0.01).name('曝光度').onChange((value) => oceanScene.setExposure(value)); + skyFolder.add(params, 'turbidity', 1, 20, 0.1).name('浑浊度').onChange((value) => oceanScene.setTurbidity(value)); + skyFolder.add(params, 'rayleigh', 0, 4, 0.01).name('瑞利散射').onChange((value) => oceanScene.setRayleigh(value)); - checkbox.addEventListener('change', (e) => { - setter(e.target.checked); - }); - }; + 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)); - bindSlider('sun-elevation', (value) => `${value.toFixed(1)}°`, (value) => oceanScene.setSunElevation(value)); - bindSlider('sun-azimuth', (value) => `${value.toFixed(1)}°`, (value) => oceanScene.setSunAzimuth(value)); - bindSlider('exposure', (value) => value.toFixed(2), (value) => oceanScene.setExposure(value)); - bindSlider('turbidity', (value) => value.toFixed(1), (value) => oceanScene.setTurbidity(value)); - bindSlider('rayleigh', (value) => value.toFixed(2), (value) => oceanScene.setRayleigh(value)); - bindSlider('bloom-strength', (value) => value.toFixed(2), (value) => oceanScene.setBloomStrength(value)); - bindSlider('bloom-radius', (value) => value.toFixed(2), (value) => oceanScene.setBloomRadius(value)); - bindSlider('cloud-coverage', (value) => value.toFixed(2), (value) => oceanScene.setCloudCoverage(value)); - bindSlider('cloud-density', (value) => value.toFixed(2), (value) => oceanScene.setCloudDensity(value)); - bindSlider('cloud-elevation', (value) => value.toFixed(2), (value) => oceanScene.setCloudElevation(value)); - bindSlider('fog-density', (value) => value.toFixed(2), (value) => oceanScene.setFogDensity(value)); - bindSlider('fog-height', (value) => value.toFixed(2), (value) => oceanScene.setFogHeight(value)); - bindSlider('fog-range', (value) => value.toFixed(2), (value) => oceanScene.setFogRange(value)); - bindSlider('rain-screen-intensity', (value) => value.toFixed(2), (value) => oceanScene.setRainScreenIntensity(value)); - bindSlider('rain-veil-intensity', (value) => value.toFixed(2), (value) => oceanScene.setRainVeilIntensity(value)); - bindSlider('rain-drop-size', (value) => value.toFixed(2), (value) => oceanScene.setRainDropSize(value)); - bindSlider('rain-speed', (value) => value.toFixed(2), (value) => oceanScene.setRainSpeed(value)); - bindSlider('rain-audio-volume', (value) => value.toFixed(2), (value) => oceanScene.setRainAudioVolume(value)); - bindSlider('snow-intensity', (value) => value.toFixed(2), (value) => oceanScene.setSnowIntensity(value)); - bindSlider('snow-speed', (value) => value.toFixed(2), (value) => oceanScene.setSnowSpeed(value)); - bindSlider('lightning-intensity', (value) => value.toFixed(2), (value) => oceanScene.setLightningIntensity(value)); - bindCheckbox('rain-enabled', (value) => oceanScene.setRainEnabled(value)); - bindCheckbox('rain-audio-enabled', (value) => oceanScene.setRainAudioEnabled(value)); - bindCheckbox('snow-enabled', (value) => oceanScene.setSnowEnabled(value)); - bindCheckbox('lightning-enabled', (value) => oceanScene.setLightningEnabled(value)); + const cloudFolder = gui.addFolder('云层'); + cloudFolder.add(params, 'cloudCoverage', 0, 1, 0.01).name('覆盖度').onChange((value) => oceanScene.setCloudCoverage(value)); + cloudFolder.add(params, 'cloudDensity', 0, 1, 0.01).name('密度').onChange((value) => oceanScene.setCloudDensity(value)); + cloudFolder.add(params, 'cloudElevation', 0, 1, 0.01).name('高度').onChange((value) => oceanScene.setCloudElevation(value)); + + const fogFolder = gui.addFolder('雾气'); + fogFolder.add(params, 'fogDensity', 0, 1, 0.01).name('浓度').onChange((value) => oceanScene.setFogDensity(value)); + fogFolder.add(params, 'fogHeight', 0, 1, 0.01).name('高度').onChange((value) => oceanScene.setFogHeight(value)); + fogFolder.add(params, 'fogRange', 0, 1, 0.01).name('范围').onChange((value) => oceanScene.setFogRange(value)); + + const rainFolder = gui.addFolder('雨效'); + rainFolder.add(params, 'rainEnabled').name('启用雨效').onChange((value) => oceanScene.setRainEnabled(value)); + rainFolder.add(params, 'rainScreenIntensity', 0, 1.5, 0.01).name('屏幕雨滴').onChange((value) => oceanScene.setRainScreenIntensity(value)); + rainFolder.add(params, 'rainVeilIntensity', 0, 1.5, 0.01).name('雨线强度').onChange((value) => oceanScene.setRainVeilIntensity(value)); + rainFolder.add(params, 'rainDropSize', 0.4, 1.8, 0.01).name('雨滴尺寸').onChange((value) => oceanScene.setRainDropSize(value)); + rainFolder.add(params, 'rainSpeed', 0.2, 2.5, 0.01).name('速度').onChange((value) => oceanScene.setRainSpeed(value)); + rainFolder.add(params, 'rainAudioEnabled').name('启用雨声').onChange((value) => oceanScene.setRainAudioEnabled(value)); + rainFolder.add(params, 'rainAudioVolume', 0, 1, 0.01).name('雨声音量').onChange((value) => oceanScene.setRainAudioVolume(value)); + rainFolder.add(params, 'lightningEnabled').name('启用雷闪').onChange((value) => oceanScene.setLightningEnabled(value)); + rainFolder.add(params, 'lightningIntensity', 0, 1.5, 0.01).name('雷闪强度').onChange((value) => oceanScene.setLightningIntensity(value)); + + const snowFolder = gui.addFolder('雪效'); + snowFolder.add(params, 'snowEnabled').name('启用降雪').onChange((value) => oceanScene.setSnowEnabled(value)); + 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(); } main().catch(console.error);