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 @@
-
-
🌊 写实无尽海洋
-
🖱️ 左键拖动旋转视角
-
🖱️ 右键拖动平移
-
🖱️ 滚轮缩放
-
☀️ 使用右上角控制太阳
-
-
-
-
☀️ 场景控制
-
-
-
-
-
-
-
-
-
-
-
雨效
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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);