From 562d0311719ec1fa746f5fc633ebc0079b255472 Mon Sep 17 00:00:00 2001 From: como Date: Thu, 26 Mar 2026 18:26:13 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OceanScene.js | 105 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/src/OceanScene.js b/src/OceanScene.js index a524e1b..b5b811a 100644 --- a/src/OceanScene.js +++ b/src/OceanScene.js @@ -762,20 +762,22 @@ export class OceanScene { initFog() { const fogTexture = this.createFogTexture(); + const lowFogTexture = this.createLowFogTexture(); this.fogGroup = new THREE.Group(); this.fogLayers = []; const layerConfigs = [ - { width: 4600, height: 2400, y: 8, opacity: 0.26, speedX: 0.00055, speedY: 0.0001, rotation: 0.08 }, - { width: 3900, height: 1900, y: 22, opacity: 0.2, speedX: -0.00032, speedY: 0.00014, rotation: -0.05 }, - { width: 3200, height: 1500, y: 42, opacity: 0.13, speedX: 0.00024, speedY: -0.00008, rotation: 0.12 } + { width: 5200, height: 1800, y: 5, opacity: 0.18, speedX: 0.00022, speedY: 0.00004, rotation: 0.03, scale: 3.2, texture: lowFogTexture, low: true }, + { width: 4300, height: 1500, y: 11, opacity: 0.14, speedX: -0.00018, speedY: 0.00005, rotation: -0.04, scale: 2.8, texture: lowFogTexture, low: true }, + { width: 4600, height: 2400, y: 22, opacity: 0.2, speedX: 0.00045, speedY: 0.0001, rotation: 0.08, scale: 2.4, texture: fogTexture, low: false }, + { width: 3900, height: 1900, y: 52, opacity: 0.14, speedX: -0.00028, speedY: 0.00014, rotation: -0.05, scale: 2.1, texture: fogTexture, low: false } ]; layerConfigs.forEach((config) => { - const texture = fogTexture.clone(); + const texture = config.texture.clone(); texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(2.4, 1.4); + texture.repeat.set(config.scale, config.low ? 1.1 : 1.4); texture.needsUpdate = true; const material = new THREE.MeshBasicMaterial({ @@ -801,6 +803,7 @@ export class OceanScene { texture, baseY: config.y, baseOpacity: config.opacity, + isLowLayer: config.low, speedX: config.speedX, speedY: config.speedY }); @@ -867,6 +870,52 @@ export class OceanScene { return texture; } + createLowFogTexture() { + const canvas = document.createElement('canvas'); + canvas.width = 512; + canvas.height = 256; + + const context = canvas.getContext('2d'); + context.clearRect(0, 0, canvas.width, canvas.height); + + for (let i = 0; i < 30; i++) { + const x = Math.random() * canvas.width; + const y = 30 + Math.random() * 150; + const radiusX = 80 + Math.random() * 140; + const radiusY = 14 + Math.random() * 28; + const gradient = context.createRadialGradient(x, y, 0, x, y, radiusX); + + gradient.addColorStop(0, 'rgba(255,255,255,0.58)'); + gradient.addColorStop(0.22, 'rgba(255,255,255,0.36)'); + gradient.addColorStop(0.6, 'rgba(255,255,255,0.12)'); + gradient.addColorStop(1, 'rgba(255,255,255,0)'); + + context.save(); + context.translate(x, y); + context.scale(1.0, radiusY / radiusX); + context.translate(-x, -y); + context.fillStyle = gradient; + context.fillRect(x - radiusX, y - radiusX, radiusX * 2, radiusX * 2); + context.restore(); + } + + const verticalFade = context.createLinearGradient(0, 0, 0, canvas.height); + verticalFade.addColorStop(0, 'rgba(255,255,255,0)'); + verticalFade.addColorStop(0.18, 'rgba(255,255,255,0.55)'); + verticalFade.addColorStop(0.52, 'rgba(255,255,255,1)'); + verticalFade.addColorStop(0.86, 'rgba(255,255,255,0.42)'); + verticalFade.addColorStop(1, 'rgba(255,255,255,0)'); + context.globalCompositeOperation = 'destination-in'; + context.fillStyle = verticalFade; + context.fillRect(0, 0, canvas.width, canvas.height); + context.globalCompositeOperation = 'source-over'; + + const texture = new THREE.CanvasTexture(canvas); + texture.colorSpace = THREE.SRGBColorSpace; + texture.needsUpdate = true; + return texture; + } + createHorizonFogTexture() { const canvas = document.createElement('canvas'); canvas.width = 64; @@ -1656,25 +1705,37 @@ export class OceanScene { const heightBase = THREE.MathUtils.lerp(-20, 95, this.params.fogHeight); const verticalSpread = THREE.MathUtils.lerp(20, 110, this.params.fogHeight); const rangeOpacity = THREE.MathUtils.lerp(0.55, 1.35, this.params.fogRange); + const nearSeaMist = THREE.MathUtils.lerp(0.12, 0.9, this.params.fogDensity) * THREE.MathUtils.lerp(0.8, 1.22, this.params.fogRange); this.fogLayers.forEach((layer, index) => { - layer.mesh.position.y = heightBase + index * verticalSpread * 0.36; - layer.mesh.scale.setScalar(THREE.MathUtils.lerp(0.82, 1.2, this.params.fogRange)); - layer.mesh.material.opacity = layer.baseOpacity * THREE.MathUtils.lerp(0.18, 1.35, this.params.fogDensity) * rangeOpacity; - layer.mesh.material.color.copy(fogLayerColor); + if (layer.isLowLayer) { + layer.mesh.position.y = THREE.MathUtils.lerp(1.5, 18.0, this.params.fogHeight) + index * 2.8; + layer.mesh.scale.set( + THREE.MathUtils.lerp(0.92, 1.28, this.params.fogRange), + 1, + THREE.MathUtils.lerp(0.92, 1.24, this.params.fogRange) + ); + layer.mesh.material.opacity = layer.baseOpacity * nearSeaMist; + layer.mesh.material.color.copy(horizonColor.clone().lerp(fogColor, 0.72)); + } else { + layer.mesh.position.y = heightBase + (index - 2) * verticalSpread * 0.42; + layer.mesh.scale.setScalar(THREE.MathUtils.lerp(0.82, 1.2, this.params.fogRange)); + layer.mesh.material.opacity = layer.baseOpacity * THREE.MathUtils.lerp(0.16, 1.18, this.params.fogDensity) * rangeOpacity; + layer.mesh.material.color.copy(fogLayerColor); + } layer.mesh.visible = layer.mesh.material.opacity > 0.01; }); if (this.horizonFog) { - this.horizonFog.material.color.copy(horizonColor); + this.horizonFog.material.color.copy(horizonColor.clone().lerp(fogColor, 0.18)); this.horizonFog.material.opacity = - THREE.MathUtils.lerp(0.16, 0.5, this.params.fogDensity) * - THREE.MathUtils.lerp(0.7, 1.28, this.params.fogRange); - this.horizonFog.position.y = THREE.MathUtils.lerp(70, 190, this.params.fogHeight); + THREE.MathUtils.lerp(0.12, 0.42, this.params.fogDensity) * + THREE.MathUtils.lerp(0.82, 1.34, this.params.fogRange); + this.horizonFog.position.y = THREE.MathUtils.lerp(52, 168, this.params.fogHeight); this.horizonFog.scale.set( - THREE.MathUtils.lerp(0.88, 1.28, this.params.fogRange), - THREE.MathUtils.lerp(0.8, 1.18, this.params.fogHeight), - THREE.MathUtils.lerp(0.88, 1.28, this.params.fogRange) + THREE.MathUtils.lerp(0.92, 1.34, this.params.fogRange), + THREE.MathUtils.lerp(0.72, 1.12, this.params.fogHeight), + THREE.MathUtils.lerp(0.92, 1.34, this.params.fogRange) ); this.horizonFog.visible = this.horizonFog.material.opacity > 0.01; } @@ -1682,13 +1743,13 @@ export class OceanScene { if (this.skyHazeBand) { this.skyHazeBand.material.color.copy(skyBlendColor); this.skyHazeBand.material.opacity = - THREE.MathUtils.lerp(0.1, 0.32, this.params.fogDensity) * - THREE.MathUtils.lerp(0.82, 1.18, this.params.fogRange); - this.skyHazeBand.position.y = THREE.MathUtils.lerp(300, 520, this.params.fogHeight); + THREE.MathUtils.lerp(0.04, 0.18, this.params.fogDensity) * + THREE.MathUtils.lerp(0.78, 1.08, this.params.fogRange); + this.skyHazeBand.position.y = THREE.MathUtils.lerp(340, 560, this.params.fogHeight); this.skyHazeBand.scale.set( - THREE.MathUtils.lerp(0.96, 1.16, this.params.fogRange), - THREE.MathUtils.lerp(0.88, 1.14, this.params.fogHeight), - THREE.MathUtils.lerp(0.96, 1.16, this.params.fogRange) + THREE.MathUtils.lerp(0.98, 1.12, this.params.fogRange), + THREE.MathUtils.lerp(0.92, 1.1, this.params.fogHeight), + THREE.MathUtils.lerp(0.98, 1.12, this.params.fogRange) ); this.skyHazeBand.visible = this.skyHazeBand.material.opacity > 0.01; }