闪电效果
This commit is contained in:
15
index.html
15
index.html
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user