调整云
This commit is contained in:
@@ -520,6 +520,7 @@ export class OceanScene {
|
||||
async initSky() {
|
||||
this.sky = new Sky();
|
||||
this.sky.scale.setScalar(10000);
|
||||
this.sky.rotation.y = Math.PI;
|
||||
this.scene.add(this.sky);
|
||||
|
||||
const skyUniforms = this.sky.material.uniforms;
|
||||
@@ -532,24 +533,55 @@ export class OceanScene {
|
||||
}
|
||||
|
||||
initClouds() {
|
||||
const cloudTexture = this.createCloudTexture();
|
||||
this.cloudGroup = new THREE.Group();
|
||||
this.cloudGroup.position.y = -120;
|
||||
this.addCloudDomeLayer(cloudTexture, {
|
||||
radius: 4200,
|
||||
this.cloudGroup.position.y = 40;
|
||||
this.addCloudPlaneLayer({
|
||||
radius: 5200,
|
||||
y: 120,
|
||||
opacity: 0.42,
|
||||
repeatX: 5.5,
|
||||
repeatY: 2.4,
|
||||
speedX: 0.00045,
|
||||
speedY: 0.00012
|
||||
scale: 1450,
|
||||
detailScale: 3.4,
|
||||
softness: 0.19,
|
||||
edgeFade: 0.2,
|
||||
shadowStrength: 0.36,
|
||||
highlightStrength: 0.18,
|
||||
erosionStrength: 0.2,
|
||||
ridgeStrength: 0.08,
|
||||
driftX: 0.0045,
|
||||
driftY: 0.0012,
|
||||
rotationZ: 0.06
|
||||
});
|
||||
this.addCloudDomeLayer(cloudTexture, {
|
||||
radius: 3900,
|
||||
this.addCloudPlaneLayer({
|
||||
radius: 4300,
|
||||
y: 250,
|
||||
opacity: 0.28,
|
||||
repeatX: 7.5,
|
||||
repeatY: 3.2,
|
||||
speedX: -0.00032,
|
||||
speedY: 0.00018
|
||||
scale: 980,
|
||||
detailScale: 4.1,
|
||||
softness: 0.17,
|
||||
edgeFade: 0.24,
|
||||
shadowStrength: 0.42,
|
||||
highlightStrength: 0.24,
|
||||
erosionStrength: 0.28,
|
||||
ridgeStrength: 0.12,
|
||||
driftX: -0.0032,
|
||||
driftY: 0.0018,
|
||||
rotationZ: -0.04
|
||||
});
|
||||
this.addCloudPlaneLayer({
|
||||
radius: 3400,
|
||||
y: 360,
|
||||
opacity: 0.18,
|
||||
scale: 760,
|
||||
detailScale: 4.8,
|
||||
softness: 0.16,
|
||||
edgeFade: 0.28,
|
||||
shadowStrength: 0.46,
|
||||
highlightStrength: 0.3,
|
||||
erosionStrength: 0.34,
|
||||
ridgeStrength: 0.16,
|
||||
driftX: 0.0021,
|
||||
driftY: -0.0014,
|
||||
rotationZ: 0.1
|
||||
});
|
||||
|
||||
this.scene.add(this.cloudGroup);
|
||||
@@ -579,44 +611,151 @@ export class OceanScene {
|
||||
this.scene.add(this.lightningCloudGlow);
|
||||
}
|
||||
|
||||
addCloudDomeLayer(texture, config) {
|
||||
const layerTexture = texture.clone();
|
||||
layerTexture.wrapS = THREE.RepeatWrapping;
|
||||
layerTexture.wrapT = THREE.RepeatWrapping;
|
||||
layerTexture.repeat.set(config.repeatX, config.repeatY);
|
||||
layerTexture.needsUpdate = true;
|
||||
addCloudPlaneLayer(config) {
|
||||
const material = new THREE.ShaderMaterial({
|
||||
uniforms: {
|
||||
tintColor: { value: new THREE.Color(0xffffff) },
|
||||
layerOpacity: { value: config.opacity },
|
||||
time: { value: 0 },
|
||||
cloudCoverage: { value: this.params.cloudCoverage },
|
||||
cloudDensity: { value: this.params.cloudDensity },
|
||||
scale: { value: config.scale },
|
||||
detailScale: { value: config.detailScale },
|
||||
softness: { value: config.softness },
|
||||
edgeFade: { value: config.edgeFade },
|
||||
drift: { value: new THREE.Vector2(config.driftX, config.driftY) },
|
||||
seed: { value: Math.random() * 100.0 },
|
||||
lightDir: { value: new THREE.Vector2(1, 0) },
|
||||
shadowStrength: { value: config.shadowStrength ?? 0.42 },
|
||||
highlightStrength: { value: config.highlightStrength ?? 0.26 },
|
||||
erosionStrength: { value: config.erosionStrength ?? 0.24 },
|
||||
ridgeStrength: { value: config.ridgeStrength ?? 0.12 }
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec2 vPlaneUv;
|
||||
varying vec3 vWorldPos;
|
||||
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: layerTexture,
|
||||
alphaMap: layerTexture,
|
||||
color: 0xffffff,
|
||||
void main() {
|
||||
vPlaneUv = uv;
|
||||
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform vec3 tintColor;
|
||||
uniform float layerOpacity;
|
||||
uniform float time;
|
||||
uniform float cloudCoverage;
|
||||
uniform float cloudDensity;
|
||||
uniform float scale;
|
||||
uniform float detailScale;
|
||||
uniform float softness;
|
||||
uniform float edgeFade;
|
||||
uniform vec2 drift;
|
||||
uniform float seed;
|
||||
uniform vec2 lightDir;
|
||||
uniform float shadowStrength;
|
||||
uniform float highlightStrength;
|
||||
uniform float erosionStrength;
|
||||
uniform float ridgeStrength;
|
||||
|
||||
varying vec2 vPlaneUv;
|
||||
varying vec3 vWorldPos;
|
||||
|
||||
float hash(vec2 p) {
|
||||
p = fract(p * vec2(123.34, 456.21));
|
||||
p += dot(p, p + 78.233);
|
||||
return fract(p.x * p.y);
|
||||
}
|
||||
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
|
||||
return mix(
|
||||
mix(hash(i + vec2(0.0, 0.0)), hash(i + vec2(1.0, 0.0)), u.x),
|
||||
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
|
||||
u.y
|
||||
);
|
||||
}
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
value += amplitude * noise(p);
|
||||
p = p * 2.03 + vec2(11.7, 7.3);
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 flow = vWorldPos.xz / scale + drift * time + vec2(seed);
|
||||
float base = fbm(flow);
|
||||
float detail = fbm(flow * detailScale + vec2(0.0, time * 0.01));
|
||||
float wisps = fbm(flow * (detailScale * 0.55) - vec2(time * 0.008, 0.0));
|
||||
float billow = fbm(flow * 0.62 + vec2(seed * 0.37, -seed * 0.21));
|
||||
float erosion = fbm(flow * (detailScale * 1.8) + vec2(seed * 1.3, -seed * 0.9));
|
||||
float ridge = 1.0 - abs(fbm(flow * (detailScale * 0.85) - vec2(seed * 0.6, seed * 0.45)) * 2.0 - 1.0);
|
||||
float shape = base * 0.5 + billow * 0.24 + detail * 0.14 + wisps * 0.12;
|
||||
shape -= erosion * erosionStrength;
|
||||
shape += ridge * ridgeStrength;
|
||||
|
||||
float densityBoost = mix(0.78, 1.28, clamp(cloudDensity, 0.0, 1.0));
|
||||
float coverageThreshold = mix(0.84, 0.34, clamp(cloudCoverage, 0.0, 1.0));
|
||||
float alpha = smoothstep(
|
||||
coverageThreshold + softness,
|
||||
coverageThreshold - softness,
|
||||
shape * densityBoost
|
||||
);
|
||||
|
||||
float core = smoothstep(
|
||||
coverageThreshold + softness * 0.4,
|
||||
coverageThreshold - softness * 1.4,
|
||||
shape * densityBoost
|
||||
);
|
||||
vec2 eps = vec2(0.03, 0.0);
|
||||
float sampleA = fbm((flow + lightDir * eps.x) * 0.62 + vec2(seed * 0.37, -seed * 0.21));
|
||||
float sampleB = fbm((flow - lightDir * eps.x) * 0.62 + vec2(seed * 0.37, -seed * 0.21));
|
||||
float lightEdge = clamp((sampleA - sampleB) * 3.2 + 0.5, 0.0, 1.0);
|
||||
float underside = 1.0 - smoothstep(0.32, 0.88, shape);
|
||||
|
||||
float radial = distance(vPlaneUv, vec2(0.5)) * 2.0;
|
||||
float edgeMask = 1.0 - smoothstep(1.0 - edgeFade, 1.0, radial);
|
||||
alpha *= edgeMask * layerOpacity;
|
||||
if (alpha <= 0.001) discard;
|
||||
|
||||
vec3 shadowColor = tintColor * (1.0 - shadowStrength);
|
||||
vec3 litColor = mix(shadowColor, tintColor, core);
|
||||
litColor += tintColor * lightEdge * core * highlightStrength;
|
||||
litColor = mix(litColor, shadowColor, underside * shadowStrength * 0.7);
|
||||
gl_FragColor = vec4(litColor, alpha);
|
||||
}
|
||||
`,
|
||||
transparent: true,
|
||||
opacity: config.opacity,
|
||||
side: THREE.BackSide,
|
||||
side: THREE.DoubleSide,
|
||||
depthWrite: false,
|
||||
alphaTest: 0.08,
|
||||
fog: false
|
||||
});
|
||||
|
||||
const geometry = new THREE.SphereGeometry(
|
||||
config.radius,
|
||||
32,
|
||||
20,
|
||||
0,
|
||||
Math.PI * 2,
|
||||
0,
|
||||
Math.PI * 0.48
|
||||
);
|
||||
|
||||
const geometry = new THREE.CircleGeometry(config.radius, 96);
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.rotation.y = Math.random() * Math.PI * 2;
|
||||
this.cloudMaterials.push(material);
|
||||
mesh.rotation.x = -Math.PI / 2;
|
||||
mesh.rotation.z = config.rotationZ ?? 0;
|
||||
mesh.position.y = config.y;
|
||||
this.cloudLayers.push({
|
||||
mesh,
|
||||
texture: layerTexture,
|
||||
speedX: config.speedX,
|
||||
speedY: config.speedY,
|
||||
baseOpacity: config.opacity
|
||||
material,
|
||||
baseOpacity: config.opacity,
|
||||
baseY: config.y,
|
||||
baseScale: config.scale,
|
||||
baseSoftness: config.softness,
|
||||
baseShadowStrength: config.shadowStrength ?? 0.42,
|
||||
baseHighlightStrength: config.highlightStrength ?? 0.26,
|
||||
baseErosionStrength: config.erosionStrength ?? 0.24,
|
||||
baseRidgeStrength: config.ridgeStrength ?? 0.12
|
||||
});
|
||||
this.cloudGroup.add(mesh);
|
||||
}
|
||||
@@ -677,36 +816,6 @@ export class OceanScene {
|
||||
this.updateFog();
|
||||
}
|
||||
|
||||
createCloudTexture() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 256;
|
||||
canvas.height = 256;
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
for (let i = 0; i < 28; i++) {
|
||||
const x = 28 + Math.random() * 200;
|
||||
const y = 52 + Math.random() * 152;
|
||||
const radius = 28 + Math.random() * 44;
|
||||
const gradient = context.createRadialGradient(x, y, 0, x, y, radius);
|
||||
|
||||
gradient.addColorStop(0, 'rgba(255,255,255,0.92)');
|
||||
gradient.addColorStop(0.28, 'rgba(255,255,255,0.78)');
|
||||
gradient.addColorStop(0.62, 'rgba(255,255,255,0.22)');
|
||||
gradient.addColorStop(1, 'rgba(255,255,255,0)');
|
||||
|
||||
context.fillStyle = gradient;
|
||||
context.fillRect(x - radius, y - radius, radius * 2, radius * 2);
|
||||
}
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas);
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
texture.needsUpdate = true;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
createLightningGlowTexture() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 256;
|
||||
@@ -1122,7 +1231,7 @@ export class OceanScene {
|
||||
setCloudElevation(value) {
|
||||
this.params.cloudElevation = value;
|
||||
if (this.cloudGroup) {
|
||||
this.cloudGroup.position.y = THREE.MathUtils.lerp(-380, 260, value);
|
||||
this.cloudGroup.position.y = THREE.MathUtils.lerp(-160, 260, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1484,20 +1593,48 @@ export class OceanScene {
|
||||
if (!this.cloudGroup) return;
|
||||
|
||||
const sunMix = THREE.MathUtils.clamp((this.params.elevation + 10) / 100, 0, 1);
|
||||
const dawnMix = 1.0 - THREE.MathUtils.smoothstep(this.params.elevation, 8, 34);
|
||||
const rainMix = this.params.rainEnabled ? THREE.MathUtils.clamp(this.params.rainVeilIntensity / 2.0, 0.35, 1.0) : 0;
|
||||
const snowMix = this.params.snowEnabled ? THREE.MathUtils.clamp(this.params.snowIntensity / 1.5, 0.35, 1.0) : 0;
|
||||
const stormMix = Math.max(rainMix, snowMix);
|
||||
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.32, 0, 1);
|
||||
cloudColor.lerp(new THREE.Color(0xbfcad6), stormMix * 0.45);
|
||||
cloudColor.lerp(new THREE.Color(0xe9f3ff), lightningMix);
|
||||
const lightDir = new THREE.Vector2(this.sun.x, this.sun.z).normalize();
|
||||
|
||||
for (const layer of this.cloudLayers) {
|
||||
this.cloudLayers.forEach((layer, index) => {
|
||||
const coverageFactor = 0.15 + this.params.cloudCoverage * 1.15;
|
||||
const densityFactor = 0.2 + this.params.cloudDensity * 1.35;
|
||||
const opacity = layer.baseOpacity * coverageFactor * densityFactor;
|
||||
layer.mesh.material.opacity = opacity;
|
||||
layer.mesh.material.color.copy(cloudColor);
|
||||
const layerDepth = index / Math.max(this.cloudLayers.length - 1, 1);
|
||||
const weatherOpacityBoost = THREE.MathUtils.lerp(1.0, 1.42 - layerDepth * 0.16, rainMix);
|
||||
const snowOpacityBoost = THREE.MathUtils.lerp(1.0, 1.22 - layerDepth * 0.08, snowMix);
|
||||
const opacity = layer.baseOpacity * coverageFactor * densityFactor * weatherOpacityBoost * snowOpacityBoost;
|
||||
const heightDrop = THREE.MathUtils.lerp(0, 120 + index * 55, rainMix);
|
||||
const snowLift = THREE.MathUtils.lerp(0, 40 + index * 24, snowMix);
|
||||
const layerScale = layer.baseScale * THREE.MathUtils.lerp(1.18 - index * 0.06, 0.9 + index * 0.04, rainMix);
|
||||
const softness = layer.baseSoftness * THREE.MathUtils.lerp(1.15, 0.82, rainMix) * THREE.MathUtils.lerp(1.0, 1.08, snowMix);
|
||||
const erosion = layer.baseErosionStrength * THREE.MathUtils.lerp(0.72 + index * 0.08, 1.28 + index * 0.1, rainMix);
|
||||
const ridge = layer.baseRidgeStrength * THREE.MathUtils.lerp(1.24, 0.78, rainMix) * THREE.MathUtils.lerp(1.0, 0.86, snowMix);
|
||||
const highlight = layer.baseHighlightStrength * THREE.MathUtils.lerp(1.45 - index * 0.12, 0.62, rainMix) * THREE.MathUtils.lerp(1.12, 0.84, snowMix);
|
||||
const shadow = layer.baseShadowStrength * THREE.MathUtils.lerp(0.94, 1.36, stormMix);
|
||||
|
||||
layer.material.uniforms.layerOpacity.value = opacity;
|
||||
layer.material.uniforms.cloudCoverage.value = this.params.cloudCoverage;
|
||||
layer.material.uniforms.cloudDensity.value = this.params.cloudDensity;
|
||||
layer.material.uniforms.scale.value = layerScale;
|
||||
layer.material.uniforms.softness.value = softness;
|
||||
layer.material.uniforms.erosionStrength.value = erosion;
|
||||
layer.material.uniforms.ridgeStrength.value = ridge;
|
||||
layer.material.uniforms.highlightStrength.value = highlight;
|
||||
layer.material.uniforms.shadowStrength.value = shadow;
|
||||
layer.material.uniforms.tintColor.value.copy(cloudColor);
|
||||
layer.material.uniforms.lightDir.value.copy(lightDir);
|
||||
layer.mesh.position.y = layer.baseY - heightDrop + snowLift + dawnMix * index * 18.0;
|
||||
layer.mesh.visible = opacity > 0.015;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateFog() {
|
||||
@@ -1571,10 +1708,8 @@ export class OceanScene {
|
||||
}
|
||||
|
||||
if (this.cloudGroup) {
|
||||
this.cloudGroup.rotation.y = time * 0.004;
|
||||
this.cloudLayers.forEach((layer) => {
|
||||
layer.texture.offset.x = time * layer.speedX;
|
||||
layer.texture.offset.y = time * layer.speedY;
|
||||
layer.material.uniforms.time.value = time;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user