闪电效果
This commit is contained in:
15
index.html
15
index.html
@@ -336,6 +336,21 @@
|
||||
</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 id="stats">FPS: <span id="fps">60</span></div>
|
||||
|
||||
@@ -26,6 +26,7 @@ export class OceanScene {
|
||||
this.renderTarget = null;
|
||||
this.sunLight = null;
|
||||
this.vegetationFillLight = null;
|
||||
this.lightningLight = null;
|
||||
this.composer = null;
|
||||
this.bloomPass = null;
|
||||
this.rainPass = null;
|
||||
@@ -57,6 +58,8 @@ export class OceanScene {
|
||||
rainVeilIntensity: 1.15,
|
||||
rainDropSize: 1.0,
|
||||
rainSpeed: 1.0,
|
||||
lightningEnabled: false,
|
||||
lightningIntensity: 0.75,
|
||||
mieCoefficient: 0.005,
|
||||
mieDirectionalG: 0.8
|
||||
};
|
||||
@@ -64,6 +67,9 @@ export class OceanScene {
|
||||
this.clock = new THREE.Clock();
|
||||
this.frameCount = 0;
|
||||
this.lastTime = performance.now();
|
||||
this.lightningFlash = 0;
|
||||
this.lightningBurstEnd = 0;
|
||||
this.nextLightningAt = 0;
|
||||
}
|
||||
|
||||
async init() {
|
||||
@@ -142,6 +148,11 @@ export class OceanScene {
|
||||
this.vegetationFillLight = new THREE.DirectionalLight(0xffb06a, 0.95);
|
||||
this.vegetationFillLight.castShadow = false;
|
||||
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() {
|
||||
@@ -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() {
|
||||
if (!this.cloudGroup) return;
|
||||
|
||||
@@ -1009,6 +1088,8 @@ export class OceanScene {
|
||||
const warmCloud = new THREE.Color(0xdab188);
|
||||
const dayCloud = new THREE.Color(0xd1dbe6);
|
||||
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) {
|
||||
const coverageFactor = 0.15 + this.params.cloudCoverage * 1.15;
|
||||
@@ -1023,6 +1104,10 @@ export class OceanScene {
|
||||
updateFog() {
|
||||
const { sunMix, fogColor, horizonColor, skyBaseColor, skyBlendColor } = this.getAtmosphereColors();
|
||||
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) {
|
||||
this.scene.fog.color.copy(fogColor);
|
||||
@@ -1077,6 +1162,7 @@ export class OceanScene {
|
||||
requestAnimationFrame(() => this.animate());
|
||||
|
||||
const time = this.clock.getElapsedTime();
|
||||
this.updateLightning(time);
|
||||
|
||||
if (this.water) {
|
||||
this.water.material.uniforms['time'].value += 1.0 / 60.0;
|
||||
@@ -1108,6 +1194,11 @@ export class OceanScene {
|
||||
this.vegetationSystem.update(time);
|
||||
}
|
||||
|
||||
if (this.lightningFlash > 0.001) {
|
||||
this.updateClouds();
|
||||
this.updateFog();
|
||||
}
|
||||
|
||||
if (this.rainPass) {
|
||||
this.rainPass.material.uniforms.time.value = time;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,9 @@ function setupControls(oceanScene) {
|
||||
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('lightning-intensity', (value) => value.toFixed(2), (value) => oceanScene.setLightningIntensity(value));
|
||||
bindCheckbox('rain-enabled', (value) => oceanScene.setRainEnabled(value));
|
||||
bindCheckbox('lightning-enabled', (value) => oceanScene.setLightningEnabled(value));
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
Reference in New Issue
Block a user