闪电效果

This commit is contained in:
2026-03-26 12:51:12 +08:00
parent eecd6cd0bd
commit 7a509d0398
3 changed files with 108 additions and 0 deletions

View File

@@ -336,6 +336,21 @@
</div> </div>
</div> </div>
</div> </div>
<div class="control-section">
<div class="control-section-title">雷闪</div>
<div class="control-toggle">
<label for="lightning-enabled">启用雷闪</label>
<input type="checkbox" id="lightning-enabled">
</div>
<div class="control-grid">
<div class="control-group full-width">
<label>雷闪强度</label>
<input type="range" id="lightning-intensity" min="0" max="1.5" value="0.75" step="0.01">
<div class="control-value" id="lightning-intensity-value">0.75</div>
</div>
</div>
</div>
</div> </div>
<div id="stats">FPS: <span id="fps">60</span></div> <div id="stats">FPS: <span id="fps">60</span></div>

View File

@@ -26,6 +26,7 @@ export class OceanScene {
this.renderTarget = null; this.renderTarget = null;
this.sunLight = null; this.sunLight = null;
this.vegetationFillLight = null; this.vegetationFillLight = null;
this.lightningLight = null;
this.composer = null; this.composer = null;
this.bloomPass = null; this.bloomPass = null;
this.rainPass = null; this.rainPass = null;
@@ -57,6 +58,8 @@ export class OceanScene {
rainVeilIntensity: 1.15, rainVeilIntensity: 1.15,
rainDropSize: 1.0, rainDropSize: 1.0,
rainSpeed: 1.0, rainSpeed: 1.0,
lightningEnabled: false,
lightningIntensity: 0.75,
mieCoefficient: 0.005, mieCoefficient: 0.005,
mieDirectionalG: 0.8 mieDirectionalG: 0.8
}; };
@@ -64,6 +67,9 @@ export class OceanScene {
this.clock = new THREE.Clock(); this.clock = new THREE.Clock();
this.frameCount = 0; this.frameCount = 0;
this.lastTime = performance.now(); this.lastTime = performance.now();
this.lightningFlash = 0;
this.lightningBurstEnd = 0;
this.nextLightningAt = 0;
} }
async init() { async init() {
@@ -142,6 +148,11 @@ export class OceanScene {
this.vegetationFillLight = new THREE.DirectionalLight(0xffb06a, 0.95); this.vegetationFillLight = new THREE.DirectionalLight(0xffb06a, 0.95);
this.vegetationFillLight.castShadow = false; this.vegetationFillLight.castShadow = false;
this.scene.add(this.vegetationFillLight); this.scene.add(this.vegetationFillLight);
this.lightningLight = new THREE.DirectionalLight(0xddeeff, 0);
this.lightningLight.castShadow = false;
this.lightningLight.position.set(-120, 180, 40);
this.scene.add(this.lightningLight);
} }
initPostProcessing() { initPostProcessing() {
@@ -1002,6 +1013,74 @@ export class OceanScene {
} }
} }
setLightningEnabled(value) {
this.params.lightningEnabled = value;
if (!value) {
this.lightningFlash = 0;
this.lightningBurstEnd = 0;
this.nextLightningAt = 0;
this.applyLightningState(0);
}
}
setLightningIntensity(value) {
this.params.lightningIntensity = value;
}
scheduleNextLightning(time) {
const rainActivity = Math.max(this.params.rainVeilIntensity, this.params.rainScreenIntensity);
const densityBias = THREE.MathUtils.clamp(rainActivity / 1.5, 0, 1);
const delay = THREE.MathUtils.lerp(7.5, 3.0, densityBias) + Math.random() * THREE.MathUtils.lerp(8.0, 4.0, densityBias);
this.nextLightningAt = time + delay;
}
updateLightning(time) {
if (!this.params.lightningEnabled) return;
if (this.nextLightningAt === 0) {
this.scheduleNextLightning(time);
}
if (time >= this.nextLightningAt && time >= this.lightningBurstEnd) {
const burstLength = 0.18 + Math.random() * 0.16;
this.lightningBurstEnd = time + burstLength;
this.lightningLight.position.set(
THREE.MathUtils.randFloat(-220, 220),
THREE.MathUtils.randFloat(140, 260),
THREE.MathUtils.randFloat(-120, 120)
);
this.scheduleNextLightning(time);
}
if (time < this.lightningBurstEnd) {
const burstProgress = 1.0 - (this.lightningBurstEnd - time) / Math.max(this.lightningBurstEnd - (this.lightningBurstEnd - 0.34), 0.0001);
const pulseA = Math.max(0, Math.sin((time + 0.13) * 37.0));
const pulseB = Math.max(0, Math.sin((time + 0.04) * 83.0));
const envelope = Math.exp(-burstProgress * 5.5);
const flash = (pulseA * 0.75 + pulseB * 0.45 + 0.25) * envelope * this.params.lightningIntensity;
this.lightningFlash = Math.max(this.lightningFlash * 0.72, flash);
} else {
this.lightningFlash *= 0.82;
if (this.lightningFlash < 0.002) this.lightningFlash = 0;
}
this.applyLightningState(this.lightningFlash);
}
applyLightningState(flash) {
if (this.lightningLight) {
this.lightningLight.intensity = flash * 5.5;
}
if (this.renderer) {
this.renderer.toneMappingExposure = this.params.exposure * (1.0 + flash * 1.6);
}
if (this.bloomPass) {
this.bloomPass.strength = this.params.bloomStrength + flash * 0.35;
}
}
updateClouds() { updateClouds() {
if (!this.cloudGroup) return; if (!this.cloudGroup) return;
@@ -1009,6 +1088,8 @@ export class OceanScene {
const warmCloud = new THREE.Color(0xdab188); const warmCloud = new THREE.Color(0xdab188);
const dayCloud = new THREE.Color(0xd1dbe6); const dayCloud = new THREE.Color(0xd1dbe6);
const cloudColor = warmCloud.lerp(dayCloud, sunMix); const cloudColor = warmCloud.lerp(dayCloud, sunMix);
const lightningMix = THREE.MathUtils.clamp(this.lightningFlash * 0.85, 0, 1);
cloudColor.lerp(new THREE.Color(0xe9f3ff), lightningMix);
for (const layer of this.cloudLayers) { for (const layer of this.cloudLayers) {
const coverageFactor = 0.15 + this.params.cloudCoverage * 1.15; const coverageFactor = 0.15 + this.params.cloudCoverage * 1.15;
@@ -1023,6 +1104,10 @@ export class OceanScene {
updateFog() { updateFog() {
const { sunMix, fogColor, horizonColor, skyBaseColor, skyBlendColor } = this.getAtmosphereColors(); const { sunMix, fogColor, horizonColor, skyBaseColor, skyBlendColor } = this.getAtmosphereColors();
const fogDensity = THREE.MathUtils.lerp(0.00015, 0.0018, this.params.fogDensity); const fogDensity = THREE.MathUtils.lerp(0.00015, 0.0018, this.params.fogDensity);
const lightningMix = THREE.MathUtils.clamp(this.lightningFlash * 0.75, 0, 1);
fogColor.lerp(new THREE.Color(0xdbe8f5), lightningMix);
horizonColor.lerp(new THREE.Color(0xe5eef9), lightningMix);
skyBlendColor.lerp(new THREE.Color(0xdbe7f3), lightningMix);
if (this.scene.fog) { if (this.scene.fog) {
this.scene.fog.color.copy(fogColor); this.scene.fog.color.copy(fogColor);
@@ -1077,6 +1162,7 @@ export class OceanScene {
requestAnimationFrame(() => this.animate()); requestAnimationFrame(() => this.animate());
const time = this.clock.getElapsedTime(); const time = this.clock.getElapsedTime();
this.updateLightning(time);
if (this.water) { if (this.water) {
this.water.material.uniforms['time'].value += 1.0 / 60.0; this.water.material.uniforms['time'].value += 1.0 / 60.0;
@@ -1108,6 +1194,11 @@ export class OceanScene {
this.vegetationSystem.update(time); this.vegetationSystem.update(time);
} }
if (this.lightningFlash > 0.001) {
this.updateClouds();
this.updateFog();
}
if (this.rainPass) { if (this.rainPass) {
this.rainPass.material.uniforms.time.value = time; this.rainPass.material.uniforms.time.value = time;
} }

View File

@@ -68,7 +68,9 @@ function setupControls(oceanScene) {
bindSlider('rain-veil-intensity', (value) => value.toFixed(2), (value) => oceanScene.setRainVeilIntensity(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-drop-size', (value) => value.toFixed(2), (value) => oceanScene.setRainDropSize(value));
bindSlider('rain-speed', (value) => value.toFixed(2), (value) => oceanScene.setRainSpeed(value)); bindSlider('rain-speed', (value) => value.toFixed(2), (value) => oceanScene.setRainSpeed(value));
bindSlider('lightning-intensity', (value) => value.toFixed(2), (value) => oceanScene.setLightningIntensity(value));
bindCheckbox('rain-enabled', (value) => oceanScene.setRainEnabled(value)); bindCheckbox('rain-enabled', (value) => oceanScene.setRainEnabled(value));
bindCheckbox('lightning-enabled', (value) => oceanScene.setLightningEnabled(value));
} }
main().catch(console.error); main().catch(console.error);