Compare commits
3 Commits
949390501c
...
64269a9088
| Author | SHA1 | Date | |
|---|---|---|---|
| 64269a9088 | |||
| 68f7e5bfb4 | |||
| 502aafc41d |
@@ -5,6 +5,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|||||||
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
||||||
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
||||||
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
|
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
|
||||||
|
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
|
||||||
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
|
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
|
||||||
import Stats from 'three/addons/libs/stats.module.js';
|
import Stats from 'three/addons/libs/stats.module.js';
|
||||||
import { TerrainGenerator } from './TerrainGenerator.js';
|
import { TerrainGenerator } from './TerrainGenerator.js';
|
||||||
@@ -43,8 +44,10 @@ export class OceanScene {
|
|||||||
this.lightningCloudGlow = null;
|
this.lightningCloudGlow = null;
|
||||||
this.composer = null;
|
this.composer = null;
|
||||||
this.bloomPass = null;
|
this.bloomPass = null;
|
||||||
|
this.fogPass = null;
|
||||||
this.rainPass = null;
|
this.rainPass = null;
|
||||||
this.snowPass = null;
|
this.snowPass = null;
|
||||||
|
this.depthTarget = null;
|
||||||
this.stats = null;
|
this.stats = null;
|
||||||
this.cloudGroup = null;
|
this.cloudGroup = null;
|
||||||
this.cloudMaterials = [];
|
this.cloudMaterials = [];
|
||||||
@@ -87,7 +90,6 @@ export class OceanScene {
|
|||||||
this.initStars();
|
this.initStars();
|
||||||
this.initNightSky();
|
this.initNightSky();
|
||||||
this.initClouds();
|
this.initClouds();
|
||||||
this.initFog();
|
|
||||||
await this.initWater();
|
await this.initWater();
|
||||||
await this.initTerrain();
|
await this.initTerrain();
|
||||||
await this.initWindTurbine();
|
await this.initWindTurbine();
|
||||||
@@ -124,7 +126,6 @@ export class OceanScene {
|
|||||||
|
|
||||||
initScene() {
|
initScene() {
|
||||||
this.scene = new THREE.Scene();
|
this.scene = new THREE.Scene();
|
||||||
this.scene.fog = new THREE.FogExp2(0x8cb8d4, 0.0006);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initCamera() {
|
initCamera() {
|
||||||
@@ -179,6 +180,13 @@ export class OceanScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initPostProcessing() {
|
initPostProcessing() {
|
||||||
|
this.depthTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
|
||||||
|
this.depthTarget.depthTexture = new THREE.DepthTexture(window.innerWidth, window.innerHeight, THREE.UnsignedIntType);
|
||||||
|
this.depthTarget.depthTexture.format = THREE.DepthFormat;
|
||||||
|
this.depthTarget.texture.minFilter = THREE.NearestFilter;
|
||||||
|
this.depthTarget.texture.magFilter = THREE.NearestFilter;
|
||||||
|
this.depthTarget.texture.generateMipmaps = false;
|
||||||
|
|
||||||
this.composer = new EffectComposer(this.renderer);
|
this.composer = new EffectComposer(this.renderer);
|
||||||
this.composer.addPass(new RenderPass(this.scene, this.camera));
|
this.composer.addPass(new RenderPass(this.scene, this.camera));
|
||||||
|
|
||||||
@@ -190,6 +198,13 @@ export class OceanScene {
|
|||||||
);
|
);
|
||||||
this.composer.addPass(this.bloomPass);
|
this.composer.addPass(this.bloomPass);
|
||||||
|
|
||||||
|
this.fogPass = new ShaderPass(this.createVolumetricFogShader());
|
||||||
|
this.fogPass.material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
|
||||||
|
this.fogPass.material.uniforms.tDepth.value = this.depthTarget.depthTexture;
|
||||||
|
this.fogPass.material.uniforms.cameraNear.value = this.camera.near;
|
||||||
|
this.fogPass.material.uniforms.cameraFar.value = this.camera.far;
|
||||||
|
this.composer.addPass(this.fogPass);
|
||||||
|
|
||||||
this.rainPass = new ShaderPass(this.createRainShader());
|
this.rainPass = new ShaderPass(this.createRainShader());
|
||||||
this.rainPass.enabled = this.params.rainEnabled;
|
this.rainPass.enabled = this.params.rainEnabled;
|
||||||
this.rainPass.material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
|
this.rainPass.material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
|
||||||
@@ -205,6 +220,8 @@ export class OceanScene {
|
|||||||
this.snowPass.material.uniforms.intensity.value = this.params.snowIntensity;
|
this.snowPass.material.uniforms.intensity.value = this.params.snowIntensity;
|
||||||
this.snowPass.material.uniforms.snowSpeed.value = this.params.snowSpeed;
|
this.snowPass.material.uniforms.snowSpeed.value = this.params.snowSpeed;
|
||||||
this.composer.addPass(this.snowPass);
|
this.composer.addPass(this.snowPass);
|
||||||
|
|
||||||
|
this.composer.addPass(new OutputPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
initAudio() {
|
initAudio() {
|
||||||
@@ -455,6 +472,134 @@ export class OceanScene {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createVolumetricFogShader() {
|
||||||
|
return {
|
||||||
|
uniforms: {
|
||||||
|
tDiffuse: { value: null },
|
||||||
|
tDepth: { value: null },
|
||||||
|
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
|
||||||
|
cameraNear: { value: this.camera?.near ?? 1 },
|
||||||
|
cameraFar: { value: this.camera?.far ?? 20000 },
|
||||||
|
projectionMatrixInverse: { value: new THREE.Matrix4() },
|
||||||
|
viewMatrixInverse: { value: new THREE.Matrix4() },
|
||||||
|
cameraWorldPosition: { value: new THREE.Vector3() },
|
||||||
|
fogColor: { value: new THREE.Color(0x9ec5db) },
|
||||||
|
horizonColor: { value: new THREE.Color(0xcfe0ee) },
|
||||||
|
fogDensity: { value: 0.0 },
|
||||||
|
fogHeight: { value: this.params.fogHeight },
|
||||||
|
fogRange: { value: this.params.fogRange },
|
||||||
|
time: { value: 0 }
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
uniform sampler2D tDiffuse;
|
||||||
|
uniform sampler2D tDepth;
|
||||||
|
uniform vec2 resolution;
|
||||||
|
uniform mat4 projectionMatrixInverse;
|
||||||
|
uniform mat4 viewMatrixInverse;
|
||||||
|
uniform vec3 cameraWorldPosition;
|
||||||
|
uniform vec3 fogColor;
|
||||||
|
uniform vec3 horizonColor;
|
||||||
|
uniform float fogDensity;
|
||||||
|
uniform float fogHeight;
|
||||||
|
uniform float fogRange;
|
||||||
|
uniform float time;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
float hash21(vec2 p) {
|
||||||
|
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return fract((p3.x + p3.y) * p3.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 reconstructWorldPosition(vec2 uv, float depth) {
|
||||||
|
float z = depth * 2.0 - 1.0;
|
||||||
|
vec4 clipPosition = vec4(uv * 2.0 - 1.0, z, 1.0);
|
||||||
|
vec4 viewPosition = projectionMatrixInverse * clipPosition;
|
||||||
|
viewPosition /= max(viewPosition.w, 0.0001);
|
||||||
|
vec4 worldPosition = viewMatrixInverse * viewPosition;
|
||||||
|
return worldPosition.xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sampleMediumDensity(vec3 samplePosition, float traveledDistance) {
|
||||||
|
float seaLevel = 2.0;
|
||||||
|
float verticalOffset = max(samplePosition.y - seaLevel, 0.0);
|
||||||
|
float heightFalloff = mix(0.08, 0.02, fogHeight);
|
||||||
|
float heightMask = exp(-verticalOffset * heightFalloff);
|
||||||
|
|
||||||
|
float seaMask = smoothstep(210.0, -12.0, samplePosition.y);
|
||||||
|
float distanceMask = smoothstep(120.0, mix(900.0, 5400.0, fogRange), traveledDistance);
|
||||||
|
float upperFade = 1.0 - smoothstep(
|
||||||
|
mix(120.0, 260.0, fogHeight),
|
||||||
|
mix(360.0, 760.0, fogHeight),
|
||||||
|
samplePosition.y
|
||||||
|
);
|
||||||
|
|
||||||
|
float windNoise = hash21(samplePosition.xz * 0.0008 + vec2(time * 0.012, -time * 0.008));
|
||||||
|
float breakup = mix(0.82, 1.14, windNoise);
|
||||||
|
|
||||||
|
return fogDensity * heightMask * seaMask * distanceMask * upperFade * breakup;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 base = texture2D(tDiffuse, vUv).rgb;
|
||||||
|
float depth = texture2D(tDepth, vUv).x;
|
||||||
|
|
||||||
|
if (fogDensity <= 0.00001) {
|
||||||
|
gl_FragColor = vec4(base, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sceneDepth = depth < 0.99999 ? depth : 0.99999;
|
||||||
|
vec3 endPosition = reconstructWorldPosition(vUv, sceneDepth);
|
||||||
|
vec3 ray = endPosition - cameraWorldPosition;
|
||||||
|
float rayLength = length(ray);
|
||||||
|
|
||||||
|
if (depth >= 0.99999) {
|
||||||
|
vec3 farPosition = reconstructWorldPosition(vUv, 0.99999);
|
||||||
|
ray = farPosition - cameraWorldPosition;
|
||||||
|
rayLength = min(length(ray), mix(2200.0, 6400.0, fogRange));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rayLength <= 0.0001) {
|
||||||
|
gl_FragColor = vec4(base, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 rayDirection = ray / rayLength;
|
||||||
|
const int STEP_COUNT = 12;
|
||||||
|
float stepLength = rayLength / float(STEP_COUNT);
|
||||||
|
float transmittance = 1.0;
|
||||||
|
vec3 inscattering = vec3(0.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < STEP_COUNT; i++) {
|
||||||
|
float jitter = hash21(gl_FragCoord.xy + float(i) * 13.37 + time * 24.0);
|
||||||
|
float traveled = (float(i) + 0.35 + jitter * 0.45) * stepLength;
|
||||||
|
vec3 samplePosition = cameraWorldPosition + rayDirection * traveled;
|
||||||
|
float localDensity = sampleMediumDensity(samplePosition, traveled);
|
||||||
|
|
||||||
|
vec3 localTint = mix(horizonColor, fogColor, smoothstep(-8.0, 140.0, samplePosition.y));
|
||||||
|
float extinction = 1.0 - exp(-localDensity * stepLength);
|
||||||
|
inscattering += transmittance * localTint * extinction;
|
||||||
|
transmittance *= exp(-localDensity * stepLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 color = base * transmittance + inscattering;
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
createSnowShader() {
|
createSnowShader() {
|
||||||
return {
|
return {
|
||||||
uniforms: {
|
uniforms: {
|
||||||
@@ -1484,7 +1629,6 @@ export class OceanScene {
|
|||||||
this.scene.environment = this.renderTarget.texture;
|
this.scene.environment = this.renderTarget.texture;
|
||||||
this.scene.add(this.sky);
|
this.scene.add(this.sky);
|
||||||
|
|
||||||
this.updateFog();
|
|
||||||
this.updateClouds();
|
this.updateClouds();
|
||||||
this.updateStars();
|
this.updateStars();
|
||||||
}
|
}
|
||||||
@@ -1513,16 +1657,20 @@ export class OceanScene {
|
|||||||
const sunMix = THREE.MathUtils.clamp((this.params.elevation + 10) / 100, 0, 1);
|
const sunMix = THREE.MathUtils.clamp((this.params.elevation + 10) / 100, 0, 1);
|
||||||
const fogColor = new THREE.Color(this.getFogColor());
|
const fogColor = new THREE.Color(this.getFogColor());
|
||||||
const nightMix = THREE.MathUtils.clamp((-this.params.elevation + 1.0) / 12.0, 0, 1);
|
const nightMix = THREE.MathUtils.clamp((-this.params.elevation + 1.0) / 12.0, 0, 1);
|
||||||
|
const clarityMix =
|
||||||
|
THREE.MathUtils.smoothstep(this.params.elevation, 52, 82) *
|
||||||
|
(1.0 - THREE.MathUtils.smoothstep(this.params.turbidity, 3.2, 8.0));
|
||||||
const warmHorizon = new THREE.Color(0xf0c7a3);
|
const warmHorizon = new THREE.Color(0xf0c7a3);
|
||||||
const coolHorizon = new THREE.Color(0xcfe0ee);
|
const coolHorizon = new THREE.Color(0xcfe0ee).lerp(new THREE.Color(0x8fd2ff), clarityMix);
|
||||||
const nightHorizon = new THREE.Color(0x27415f);
|
const nightHorizon = new THREE.Color(0x27415f);
|
||||||
const horizonColor = warmHorizon.clone().lerp(coolHorizon, sunMix).lerp(nightHorizon, nightMix);
|
const horizonColor = warmHorizon.clone().lerp(coolHorizon, sunMix).lerp(nightHorizon, nightMix);
|
||||||
const warmSkyBase = new THREE.Color(0xf6d7b8);
|
const warmSkyBase = new THREE.Color(0xf6d7b8);
|
||||||
const coolSkyBase = new THREE.Color(0xbfd8eb);
|
const coolSkyBase = new THREE.Color(0xbfd8eb).lerp(new THREE.Color(0x6fc4ff), clarityMix);
|
||||||
const nightSkyBase = new THREE.Color(0x08111f);
|
const nightSkyBase = new THREE.Color(0x08111f);
|
||||||
const nightSkyBlend = new THREE.Color(0x182940);
|
const nightSkyBlend = new THREE.Color(0x182940);
|
||||||
const skyBaseColor = warmSkyBase.clone().lerp(coolSkyBase, sunMix * 0.92).lerp(nightSkyBase, nightMix);
|
const skyBaseColor = warmSkyBase.clone().lerp(coolSkyBase, sunMix * 0.92).lerp(nightSkyBase, nightMix);
|
||||||
const skyBlendColor = skyBaseColor.clone().lerp(fogColor, 0.42).lerp(nightSkyBlend, nightMix * 0.78);
|
const fogBlend = THREE.MathUtils.lerp(0.42, 0.16, clarityMix);
|
||||||
|
const skyBlendColor = skyBaseColor.clone().lerp(fogColor, fogBlend).lerp(nightSkyBlend, nightMix * 0.78);
|
||||||
|
|
||||||
return { sunMix, fogColor, horizonColor, skyBaseColor, skyBlendColor };
|
return { sunMix, fogColor, horizonColor, skyBaseColor, skyBlendColor };
|
||||||
}
|
}
|
||||||
@@ -1535,9 +1683,17 @@ export class OceanScene {
|
|||||||
this.camera.aspect = window.innerWidth / window.innerHeight;
|
this.camera.aspect = window.innerWidth / window.innerHeight;
|
||||||
this.camera.updateProjectionMatrix();
|
this.camera.updateProjectionMatrix();
|
||||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
if (this.depthTarget) {
|
||||||
|
this.depthTarget.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
}
|
||||||
if (this.composer) {
|
if (this.composer) {
|
||||||
this.composer.setSize(window.innerWidth, window.innerHeight);
|
this.composer.setSize(window.innerWidth, window.innerHeight);
|
||||||
}
|
}
|
||||||
|
if (this.fogPass) {
|
||||||
|
this.fogPass.material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
|
||||||
|
this.fogPass.material.uniforms.cameraNear.value = this.camera.near;
|
||||||
|
this.fogPass.material.uniforms.cameraFar.value = this.camera.far;
|
||||||
|
}
|
||||||
if (this.rainPass) {
|
if (this.rainPass) {
|
||||||
this.rainPass.material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
|
this.rainPass.material.uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
|
||||||
}
|
}
|
||||||
@@ -1618,8 +1774,13 @@ export class OceanScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFogEnabled(value) {
|
||||||
|
this.params.fogEnabled = value;
|
||||||
|
this.updateFog();
|
||||||
|
}
|
||||||
|
|
||||||
setFogDensity(value) {
|
setFogDensity(value) {
|
||||||
this.params.fogDensity = value;
|
this.params.fogDensity = THREE.MathUtils.clamp(value, 0, 2);
|
||||||
this.updateFog();
|
this.updateFog();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1823,6 +1984,7 @@ export class OceanScene {
|
|||||||
this.setCloudCoverage(mergedParams.cloudCoverage);
|
this.setCloudCoverage(mergedParams.cloudCoverage);
|
||||||
this.setCloudDensity(mergedParams.cloudDensity);
|
this.setCloudDensity(mergedParams.cloudDensity);
|
||||||
this.setCloudElevation(mergedParams.cloudElevation);
|
this.setCloudElevation(mergedParams.cloudElevation);
|
||||||
|
this.setFogEnabled(mergedParams.fogEnabled ?? true);
|
||||||
this.setFogDensity(mergedParams.fogDensity);
|
this.setFogDensity(mergedParams.fogDensity);
|
||||||
this.setFogHeight(mergedParams.fogHeight);
|
this.setFogHeight(mergedParams.fogHeight);
|
||||||
this.setFogRange(mergedParams.fogRange);
|
this.setFogRange(mergedParams.fogRange);
|
||||||
@@ -2088,75 +2250,51 @@ export class OceanScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFog() {
|
updateFog() {
|
||||||
const { sunMix, fogColor, horizonColor, skyBaseColor, skyBlendColor } = this.getAtmosphereColors();
|
if (!this.fogPass) return;
|
||||||
const fogDensity = THREE.MathUtils.lerp(0.00015, 0.0018, this.params.fogDensity);
|
|
||||||
|
const { fogColor, horizonColor } = this.getAtmosphereColors();
|
||||||
const lightningMix = THREE.MathUtils.clamp(this.lightningFlash * 0.42, 0, 1);
|
const lightningMix = THREE.MathUtils.clamp(this.lightningFlash * 0.42, 0, 1);
|
||||||
fogColor.lerp(new THREE.Color(0xdbe8f5), lightningMix);
|
fogColor.lerp(new THREE.Color(0xdbe8f5), lightningMix);
|
||||||
horizonColor.lerp(new THREE.Color(0xe5eef9), lightningMix);
|
horizonColor.lerp(new THREE.Color(0xe5eef9), lightningMix);
|
||||||
skyBlendColor.lerp(new THREE.Color(0xdbe7f3), lightningMix);
|
|
||||||
|
|
||||||
if (this.scene.fog) {
|
const uniforms = this.fogPass.material.uniforms;
|
||||||
this.scene.fog.color.copy(fogColor);
|
uniforms.fogColor.value.copy(fogColor);
|
||||||
this.scene.fog.density = fogDensity * THREE.MathUtils.lerp(0.7, 1.4, this.params.fogRange);
|
uniforms.horizonColor.value.copy(horizonColor);
|
||||||
}
|
uniforms.fogDensity.value = this.params.fogEnabled
|
||||||
|
? THREE.MathUtils.lerp(0.00002, 0.00078, this.params.fogDensity / 2.0)
|
||||||
|
: 0.0;
|
||||||
|
uniforms.fogHeight.value = this.params.fogHeight;
|
||||||
|
uniforms.fogRange.value = this.params.fogRange;
|
||||||
|
uniforms.projectionMatrixInverse.value.copy(this.camera.projectionMatrixInverse);
|
||||||
|
uniforms.viewMatrixInverse.value.copy(this.camera.matrixWorld);
|
||||||
|
uniforms.cameraWorldPosition.value.copy(this.camera.position);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.fogGroup) return;
|
updateDepthTarget() {
|
||||||
|
if (!this.depthTarget) return;
|
||||||
|
|
||||||
const fogLayerColor = horizonColor.clone().lerp(fogColor, 0.55);
|
const hiddenObjects = [
|
||||||
const heightBase = THREE.MathUtils.lerp(-20, 95, this.params.fogHeight);
|
this.sky,
|
||||||
const verticalSpread = THREE.MathUtils.lerp(20, 110, this.params.fogHeight);
|
this.starField,
|
||||||
const rangeOpacity = THREE.MathUtils.lerp(0.55, 1.35, this.params.fogRange);
|
this.moonSprite,
|
||||||
const nearSeaMist = THREE.MathUtils.lerp(0.12, 0.9, this.params.fogDensity) * THREE.MathUtils.lerp(0.8, 1.22, this.params.fogRange);
|
this.moonGlowSprite,
|
||||||
|
this.galaxyBand
|
||||||
|
].filter(Boolean);
|
||||||
|
const previousVisibility = hiddenObjects.map((object) => object.visible);
|
||||||
|
|
||||||
this.fogLayers.forEach((layer, index) => {
|
hiddenObjects.forEach((object) => {
|
||||||
if (layer.isLowLayer) {
|
object.visible = false;
|
||||||
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) {
|
const previousRenderTarget = this.renderer.getRenderTarget();
|
||||||
this.horizonFog.material.color.copy(horizonColor.clone().lerp(fogColor, 0.18));
|
this.renderer.setRenderTarget(this.depthTarget);
|
||||||
this.horizonFog.material.opacity =
|
this.renderer.clear();
|
||||||
THREE.MathUtils.lerp(0.12, 0.42, this.params.fogDensity) *
|
this.renderer.render(this.scene, this.camera);
|
||||||
THREE.MathUtils.lerp(0.82, 1.34, this.params.fogRange);
|
this.renderer.setRenderTarget(previousRenderTarget);
|
||||||
this.horizonFog.position.y = THREE.MathUtils.lerp(52, 168, this.params.fogHeight);
|
|
||||||
this.horizonFog.scale.set(
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.skyHazeBand) {
|
hiddenObjects.forEach((object, index) => {
|
||||||
this.skyHazeBand.material.color.copy(skyBlendColor);
|
object.visible = previousVisibility[index];
|
||||||
this.skyHazeBand.material.opacity =
|
});
|
||||||
THREE.MathUtils.lerp(0.04, 0.18, this.params.fogDensity) *
|
|
||||||
THREE.MathUtils.lerp(0.78, 1.08, this.params.fogRange);
|
|
||||||
const nightHaze = THREE.MathUtils.clamp((-this.params.elevation + 1.0) / 12.0, 0, 1);
|
|
||||||
this.skyHazeBand.material.color.lerp(new THREE.Color(0x203754), nightHaze * 0.72);
|
|
||||||
this.skyHazeBand.material.opacity += nightHaze * 0.12;
|
|
||||||
this.skyHazeBand.position.y = THREE.MathUtils.lerp(340, 560, this.params.fogHeight);
|
|
||||||
this.skyHazeBand.scale.set(
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
animate() {
|
animate() {
|
||||||
@@ -2183,20 +2321,6 @@ export class OceanScene {
|
|||||||
this.starField.material.uniforms.time.value = time;
|
this.starField.material.uniforms.time.value = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fogGroup) {
|
|
||||||
this.fogLayers.forEach((layer, index) => {
|
|
||||||
layer.texture.offset.x = time * layer.speedX;
|
|
||||||
layer.texture.offset.y = time * layer.speedY;
|
|
||||||
layer.mesh.rotation.z += Math.sin(time * 0.05 + index) * 0.00002;
|
|
||||||
});
|
|
||||||
if (this.horizonFog?.userData.texture) {
|
|
||||||
this.horizonFog.userData.texture.offset.x = time * 0.00035;
|
|
||||||
}
|
|
||||||
if (this.skyHazeBand?.userData.texture) {
|
|
||||||
this.skyHazeBand.userData.texture.offset.x = -time * 0.00018;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.vegetationSystem) {
|
if (this.vegetationSystem) {
|
||||||
this.vegetationSystem.update(time);
|
this.vegetationSystem.update(time);
|
||||||
}
|
}
|
||||||
@@ -2218,9 +2342,14 @@ export class OceanScene {
|
|||||||
if (this.snowPass) {
|
if (this.snowPass) {
|
||||||
this.snowPass.material.uniforms.time.value = time;
|
this.snowPass.material.uniforms.time.value = time;
|
||||||
}
|
}
|
||||||
|
if (this.fogPass) {
|
||||||
|
this.fogPass.material.uniforms.time.value = time;
|
||||||
|
}
|
||||||
|
|
||||||
this.controls.update();
|
this.controls.update();
|
||||||
|
this.updateFog();
|
||||||
if (this.composer) {
|
if (this.composer) {
|
||||||
|
this.updateDepthTarget();
|
||||||
this.composer.render();
|
this.composer.render();
|
||||||
} else {
|
} else {
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer.render(this.scene, this.camera);
|
||||||
|
|||||||
17
src/main.js
17
src/main.js
@@ -54,9 +54,8 @@ function setupControls(oceanScene) {
|
|||||||
'cloudCoverage',
|
'cloudCoverage',
|
||||||
'cloudDensity',
|
'cloudDensity',
|
||||||
'cloudElevation',
|
'cloudElevation',
|
||||||
|
'fogEnabled',
|
||||||
'fogDensity',
|
'fogDensity',
|
||||||
'fogHeight',
|
|
||||||
'fogRange',
|
|
||||||
'rainEnabled',
|
'rainEnabled',
|
||||||
'rainScreenIntensity',
|
'rainScreenIntensity',
|
||||||
'rainVeilIntensity',
|
'rainVeilIntensity',
|
||||||
@@ -85,9 +84,8 @@ function setupControls(oceanScene) {
|
|||||||
cloudCoverage: '云层覆盖度',
|
cloudCoverage: '云层覆盖度',
|
||||||
cloudDensity: '云层密度',
|
cloudDensity: '云层密度',
|
||||||
cloudElevation: '云层高度',
|
cloudElevation: '云层高度',
|
||||||
|
fogEnabled: '是否启用雾气',
|
||||||
fogDensity: '雾气浓度',
|
fogDensity: '雾气浓度',
|
||||||
fogHeight: '雾气高度',
|
|
||||||
fogRange: '雾气范围',
|
|
||||||
rainEnabled: '是否启用雨效',
|
rainEnabled: '是否启用雨效',
|
||||||
rainScreenIntensity: '屏幕雨滴强度',
|
rainScreenIntensity: '屏幕雨滴强度',
|
||||||
rainVeilIntensity: '雨线强度',
|
rainVeilIntensity: '雨线强度',
|
||||||
@@ -202,11 +200,6 @@ function setupControls(oceanScene) {
|
|||||||
bindController(cloudFolder.add(params, 'cloudDensity', 0, 1, 0.01).name('密度'), (value) => oceanScene.setCloudDensity(value));
|
bindController(cloudFolder.add(params, 'cloudDensity', 0, 1, 0.01).name('密度'), (value) => oceanScene.setCloudDensity(value));
|
||||||
bindController(cloudFolder.add(params, 'cloudElevation', 0, 1, 0.01).name('高度'), (value) => oceanScene.setCloudElevation(value));
|
bindController(cloudFolder.add(params, 'cloudElevation', 0, 1, 0.01).name('高度'), (value) => oceanScene.setCloudElevation(value));
|
||||||
|
|
||||||
const fogFolder = gui.addFolder('雾气');
|
|
||||||
bindController(fogFolder.add(params, 'fogDensity', 0, 1, 0.01).name('浓度'), (value) => oceanScene.setFogDensity(value));
|
|
||||||
bindController(fogFolder.add(params, 'fogHeight', 0, 1, 0.01).name('高度'), (value) => oceanScene.setFogHeight(value));
|
|
||||||
bindController(fogFolder.add(params, 'fogRange', 0, 1, 0.01).name('范围'), (value) => oceanScene.setFogRange(value));
|
|
||||||
|
|
||||||
const rainFolder = gui.addFolder('雨效');
|
const rainFolder = gui.addFolder('雨效');
|
||||||
bindController(rainFolder.add(params, 'rainEnabled').name('启用雨效'), (value) => oceanScene.setRainEnabled(value));
|
bindController(rainFolder.add(params, 'rainEnabled').name('启用雨效'), (value) => oceanScene.setRainEnabled(value));
|
||||||
bindController(rainFolder.add(params, 'rainVeilIntensity', 0.5, 2.5, 0.01).name('雨线强度'), (value) => oceanScene.setRainVeilIntensity(value));
|
bindController(rainFolder.add(params, 'rainVeilIntensity', 0.5, 2.5, 0.01).name('雨线强度'), (value) => oceanScene.setRainVeilIntensity(value));
|
||||||
@@ -214,12 +207,16 @@ function setupControls(oceanScene) {
|
|||||||
bindController(rainFolder.add(params, 'rainAudioVolume', 0, 1, 0.01).name('雨声音量'), (value) => oceanScene.setRainAudioVolume(value));
|
bindController(rainFolder.add(params, 'rainAudioVolume', 0, 1, 0.01).name('雨声音量'), (value) => oceanScene.setRainAudioVolume(value));
|
||||||
bindController(rainFolder.add(params, 'lightningEnabled').name('启用雷闪'), (value) => oceanScene.setLightningEnabled(value));
|
bindController(rainFolder.add(params, 'lightningEnabled').name('启用雷闪'), (value) => oceanScene.setLightningEnabled(value));
|
||||||
|
|
||||||
|
const fogFolder = gui.addFolder('雾气');
|
||||||
|
bindController(fogFolder.add(params, 'fogEnabled').name('启用雾气'), (value) => oceanScene.setFogEnabled(value));
|
||||||
|
bindController(fogFolder.add(params, 'fogDensity', 0, 2, 0.01).name('雾气浓度'), (value) => oceanScene.setFogDensity(value));
|
||||||
|
|
||||||
const snowFolder = gui.addFolder('雪效');
|
const snowFolder = gui.addFolder('雪效');
|
||||||
bindController(snowFolder.add(params, 'snowEnabled').name('启用降雪'), (value) => oceanScene.setSnowEnabled(value));
|
bindController(snowFolder.add(params, 'snowEnabled').name('启用降雪'), (value) => oceanScene.setSnowEnabled(value));
|
||||||
bindController(snowFolder.add(params, 'snowIntensity', 0, 1.5, 0.01).name('雪量'), (value) => oceanScene.setSnowIntensity(value));
|
bindController(snowFolder.add(params, 'snowIntensity', 0, 1.5, 0.01).name('雪量'), (value) => oceanScene.setSnowIntensity(value));
|
||||||
bindController(snowFolder.add(params, 'snowSpeed', 0.2, 2.2, 0.01).name('速度'), (value) => oceanScene.setSnowSpeed(value));
|
bindController(snowFolder.add(params, 'snowSpeed', 0.2, 2.2, 0.01).name('速度'), (value) => oceanScene.setSnowSpeed(value));
|
||||||
|
|
||||||
gui.close();
|
[skyFolder, bloomFolder, waterFolder, cloudFolder, rainFolder, fogFolder, snowFolder].forEach((folder) => folder.close());
|
||||||
updateStarControllerState();
|
updateStarControllerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const DEFAULT_SCENE_PARAMS = {
|
|||||||
cloudCoverage: 0.26,
|
cloudCoverage: 0.26,
|
||||||
cloudDensity: 0.38,
|
cloudDensity: 0.38,
|
||||||
cloudElevation: 0.66,
|
cloudElevation: 0.66,
|
||||||
|
fogEnabled: true,
|
||||||
fogDensity: 0.16,
|
fogDensity: 0.16,
|
||||||
fogHeight: 0.26,
|
fogHeight: 0.26,
|
||||||
fogRange: 0.38,
|
fogRange: 0.38,
|
||||||
@@ -68,18 +69,18 @@ export const WEATHER_PRESETS = {
|
|||||||
...DEFAULT_SCENE_PARAMS,
|
...DEFAULT_SCENE_PARAMS,
|
||||||
elevation: 72,
|
elevation: 72,
|
||||||
azimuth: 180,
|
azimuth: 180,
|
||||||
exposure: 0.27,
|
exposure: 0.45,
|
||||||
turbidity: 8.2,
|
turbidity: 2.2,
|
||||||
rayleigh: 2.1,
|
rayleigh: 0.18,
|
||||||
bloomStrength: 0.14,
|
bloomStrength: 0.2,
|
||||||
bloomRadius: 0.06,
|
bloomRadius: 0.03,
|
||||||
waterColor: '#2b78a4',
|
waterColor: '#25b7d9',
|
||||||
cloudCoverage: 0.18,
|
cloudCoverage: 0.06,
|
||||||
cloudDensity: 0.28,
|
cloudDensity: 0.12,
|
||||||
cloudElevation: 0.7,
|
cloudElevation: 0.82,
|
||||||
fogDensity: 0.1,
|
fogDensity: 0.015,
|
||||||
fogHeight: 0.2,
|
fogHeight: 0.12,
|
||||||
fogRange: 0.24,
|
fogRange: 0.08,
|
||||||
starIntensity: 0.0,
|
starIntensity: 0.0,
|
||||||
lightningEnabled: false
|
lightningEnabled: false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user