fix(studio): единая система неба — убрать второе (жёлтое) солнце
Environment больше НЕ рисует свою жёлтую сферу-солнце/луну/фон (флаг _drawSkyBodies=false) — иначе на небе было два солнца. Единое небо рисует SkyboxManager (купол + солнечный диск + облака + горы). SkyboxManager стал единым источником освещения: каждый пресет выставляет direction/intensity/ color солнца и ambient (lights переданы в конструктор), fadeTo плавно ведёт и свет. Environment оставлен только для day/night cycle совместимости. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
71536668f2
commit
a4881ee5ce
@ -1293,7 +1293,7 @@ export class BabylonScene {
|
||||
}
|
||||
this.dynamics = new DynamicsManager(this);
|
||||
this.environment = new Environment(this.scene, this._hemiLight, this._sunLight);
|
||||
this.skybox = new SkyboxManager(this.scene); // задача 16 — кастомное небо
|
||||
this.skybox = new SkyboxManager(this.scene, this._hemiLight, this._sunLight); // задача 16 — кастомное небо (единый источник света)
|
||||
this.audioManager = new AudioManager();
|
||||
this.assetManager = new AssetManager();
|
||||
// PrimitiveManager должен уметь брать dataURL картинки по id ассета,
|
||||
|
||||
@ -91,10 +91,15 @@ export class Environment {
|
||||
this.fogEnabled = false;
|
||||
this.fogColor = [0.7, 0.8, 0.9];
|
||||
this.fogDensity = 0.01;
|
||||
// Видимые тела на небе (солнце и луна) — создаём по запросу
|
||||
// Видимые тела на небе (солнце и луна).
|
||||
// ВАЖНО (задача 16): единое небо рисует SkyboxManager (купол + солнечный
|
||||
// диск + облака). Environment больше НЕ рисует свою жёлтую сферу/луну/фон —
|
||||
// иначе на небе два солнца. Environment теперь отвечает ТОЛЬКО за свет
|
||||
// (направление/яркость солнца, ambient). Флаг ниже отключает небесные тела.
|
||||
this._drawSkyBodies = false;
|
||||
this._sunMesh = null;
|
||||
this._moonMesh = null;
|
||||
this._createSkyBodies();
|
||||
if (this._drawSkyBodies) this._createSkyBodies();
|
||||
this._applyTime();
|
||||
}
|
||||
|
||||
|
||||
@ -99,6 +99,7 @@ const PRESETS = {
|
||||
mountains: false,
|
||||
clouds: { enabled: true, cover: 0.25, color: '#ffffff', speed: 0.015 },
|
||||
fog: { color: '#cfe2f2', density: 0.0035 },
|
||||
light: { sunIntensity: 1.0, sunColor: '#fff6e0', hemiIntensity: 0.7, ambient: '#b9d4ee' },
|
||||
},
|
||||
'lowpoly-roblox': {
|
||||
top: '#4a93e6', horizon: '#cfe6f5', bottom: '#e6f1fa',
|
||||
@ -106,6 +107,7 @@ const PRESETS = {
|
||||
mountains: true,
|
||||
clouds: { enabled: true, cover: 0.45, color: '#ffffff', speed: 0.012 },
|
||||
fog: { color: '#e2eef7', density: 0.005 },
|
||||
light: { sunIntensity: 0.95, sunColor: '#fff7e0', hemiIntensity: 0.75, ambient: '#cfe6f5' },
|
||||
},
|
||||
'cloudy': {
|
||||
top: '#8fa6bd', horizon: '#c2ccd6', bottom: '#d8dde2',
|
||||
@ -113,6 +115,7 @@ const PRESETS = {
|
||||
mountains: false,
|
||||
clouds: { enabled: true, cover: 0.8, color: '#e8ebef', speed: 0.02 },
|
||||
fog: { color: '#cfd6dd', density: 0.008 },
|
||||
light: { sunIntensity: 0.5, sunColor: '#dfe4ea', hemiIntensity: 0.8, ambient: '#c2ccd6' },
|
||||
},
|
||||
'sunset': {
|
||||
top: '#2a3a6b', horizon: '#f5915a', bottom: '#f7c98a',
|
||||
@ -120,6 +123,7 @@ const PRESETS = {
|
||||
mountains: true,
|
||||
clouds: { enabled: true, cover: 0.35, color: '#ffd9b3', speed: 0.01 },
|
||||
fog: { color: '#f0b483', density: 0.006 },
|
||||
light: { sunIntensity: 0.7, sunColor: '#ff9a52', hemiIntensity: 0.5, ambient: '#9a6a78' },
|
||||
},
|
||||
'starry-night': {
|
||||
top: '#070b1f', horizon: '#1b2547', bottom: '#243056',
|
||||
@ -127,6 +131,7 @@ const PRESETS = {
|
||||
mountains: true, stars: true,
|
||||
clouds: { enabled: false },
|
||||
fog: { color: '#141c38', density: 0.004 },
|
||||
light: { sunIntensity: 0.18, sunColor: '#9aa8d8', hemiIntensity: 0.25, ambient: '#1b2547' },
|
||||
},
|
||||
'space': {
|
||||
top: '#02030a', horizon: '#06070f', bottom: '#0a0c18',
|
||||
@ -134,12 +139,15 @@ const PRESETS = {
|
||||
mountains: false, stars: true,
|
||||
clouds: { enabled: false },
|
||||
fog: { enabled: false },
|
||||
light: { sunIntensity: 0.6, sunColor: '#ffffff', hemiIntensity: 0.2, ambient: '#10121c' },
|
||||
},
|
||||
};
|
||||
|
||||
export class SkyboxManager {
|
||||
constructor(scene) {
|
||||
constructor(scene, hemiLight, sunLight) {
|
||||
this.scene = scene;
|
||||
this.hemiLight = hemiLight || null; // ambient
|
||||
this.sunLight = sunLight || null; // directional (тени)
|
||||
this._dome = null;
|
||||
this._mat = null;
|
||||
this._mountains = null;
|
||||
@ -160,6 +168,7 @@ export class SkyboxManager {
|
||||
mountains: false, stars: false,
|
||||
clouds: { enabled: false, cover: 0.4, color: '#ffffff', speed: 0.012 },
|
||||
fog: { enabled: false, color: '#dde8f2', density: 0.005 },
|
||||
light: { sunIntensity: 0.95, sunColor: '#fff7e0', hemiIntensity: 0.75, ambient: '#cfe6f5' },
|
||||
};
|
||||
}
|
||||
|
||||
@ -352,6 +361,25 @@ export class SkyboxManager {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Освещение (единый источник: небо управляет светом сцены) ─────────────
|
||||
/** Выставить направление/яркость солнца и ambient под текущее небо. */
|
||||
_applyLighting(light, sunDir) {
|
||||
if (this.sunLight && sunDir) {
|
||||
// DirectionalLight.direction указывает КУДА падает свет → от солнца вниз.
|
||||
const d = new Vector3(-sunDir[0], -sunDir[1], -sunDir[2]);
|
||||
if (d.lengthSquared() > 0) { d.normalize(); this.sunLight.direction = d; }
|
||||
}
|
||||
if (!light) return;
|
||||
if (this.sunLight) {
|
||||
if (typeof light.sunIntensity === 'number') this.sunLight.intensity = light.sunIntensity;
|
||||
if (light.sunColor) this.sunLight.diffuse = Color3.FromArray(hexToRgb(light.sunColor));
|
||||
}
|
||||
if (this.hemiLight) {
|
||||
if (typeof light.hemiIntensity === 'number') this.hemiLight.intensity = light.hemiIntensity;
|
||||
if (light.ambient) this.hemiLight.groundColor = Color3.FromArray(hexToRgb(light.ambient));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Public API ───────────────────────────────────────────────────────────
|
||||
|
||||
/** Применить пресет или ручные опции gradient. */
|
||||
@ -365,6 +393,8 @@ export class SkyboxManager {
|
||||
s.haze = preset.haze; s.mountains = !!preset.mountains; s.stars = !!preset.stars;
|
||||
s.clouds = { ...(preset.clouds || { enabled: false }) };
|
||||
s.fog = { enabled: preset.fog?.enabled !== false, ...(preset.fog || {}) };
|
||||
s.light = preset.light || null;
|
||||
this._applyLighting(preset.light, preset.sunDir);
|
||||
} else {
|
||||
// Ручной gradient: { topColor, bottomColor, horizonColor, sunDirection, sunColor, sunSize }
|
||||
if (opts.topColor) s.top = opts.topColor;
|
||||
@ -428,13 +458,27 @@ export class SkyboxManager {
|
||||
s.mountains = !!target.mountains; s.stars = !!target.stars;
|
||||
s.clouds = { ...(target.clouds || { enabled: false }) };
|
||||
s.fog = { enabled: target.fog?.enabled !== false, ...(target.fog || {}) };
|
||||
s.light = target.light || null;
|
||||
this._rebuildExtras();
|
||||
// Запоминаем стартовые/целевые значения света для плавной анимации.
|
||||
if (target.light) {
|
||||
this._fade.lightFrom = {
|
||||
sunInt: this.sunLight?.intensity ?? 1,
|
||||
hemiInt: this.hemiLight?.intensity ?? 0.7,
|
||||
};
|
||||
this._fade.lightTo = {
|
||||
sunInt: target.light.sunIntensity ?? 1,
|
||||
hemiInt: target.light.hemiIntensity ?? 0.7,
|
||||
sunColor: target.light.sunColor, ambient: target.light.ambient,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Пересобрать всё (купол-uniforms + горы + звёзды + облака + туман). */
|
||||
/** Пересобрать всё (купол-uniforms + горы + звёзды + облака + туман + свет). */
|
||||
_rebuildAll() {
|
||||
this._applyShaderUniforms();
|
||||
this._rebuildExtras();
|
||||
this._applyLighting(this._state.light, this._state.sunDir);
|
||||
}
|
||||
|
||||
_rebuildExtras() {
|
||||
@ -471,6 +515,23 @@ export class SkyboxManager {
|
||||
m.setVector3('sunDir', new Vector3(sd[0], sd[1], sd[2]).normalize());
|
||||
m.setFloat('sunSize', f.sunSize + (t.sunSize - f.sunSize) * k);
|
||||
m.setFloat('horizonHaze', f.haze + (t.haze - f.haze) * k);
|
||||
// Плавно ведём направление солнца (свет) к целевому (используем sd выше).
|
||||
if (this.sunLight) {
|
||||
const d = new Vector3(-sd[0], -sd[1], -sd[2]);
|
||||
if (d.lengthSquared() > 0) { d.normalize(); this.sunLight.direction = d; }
|
||||
}
|
||||
}
|
||||
// Плавно ведём яркость/ambient света.
|
||||
if (this._fade.lightFrom && this._fade.lightTo) {
|
||||
const lf = this._fade.lightFrom, lt = this._fade.lightTo;
|
||||
if (this.sunLight) {
|
||||
this.sunLight.intensity = lf.sunInt + (lt.sunInt - lf.sunInt) * k;
|
||||
if (lt.sunColor) this.sunLight.diffuse = Color3.FromArray(hexToRgb(lt.sunColor));
|
||||
}
|
||||
if (this.hemiLight) {
|
||||
this.hemiLight.intensity = lf.hemiInt + (lt.hemiInt - lf.hemiInt) * k;
|
||||
if (lt.ambient) this.hemiLight.groundColor = Color3.FromArray(hexToRgb(lt.ambient));
|
||||
}
|
||||
}
|
||||
if (k >= 1) {
|
||||
// Зафиксировать целевое состояние в _state (как hex).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user