diff --git a/atmosphere/src/App.jsx b/atmosphere/src/App.jsx index 3b6c48996..ed8c8534a 100644 --- a/atmosphere/src/App.jsx +++ b/atmosphere/src/App.jsx @@ -1,9 +1,9 @@ -import React, { useEffect, useRef, Suspense } from 'react'; +import React, { useEffect, useRef, Suspense, useState } from 'react'; import { Canvas, useFrame, useLoader, useThree } from '@react-three/fiber'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { VRMLoaderPlugin, VRMUtils } from '@pixiv/three-vrm'; import { createVRMAnimationClip, VRMAnimationLoaderPlugin } from '@pixiv/three-vrm-animation'; -import { AnimationMixer, Vector3 } from 'three'; +import { AnimationMixer, Vector3, MathUtils } from 'three'; import { OrbitControls, PerspectiveCamera } from '@react-three/drei'; import { EffectComposer, ToneMapping } from '@react-three/postprocessing'; import { ToneMappingMode } from 'postprocessing'; @@ -18,81 +18,144 @@ const VRM_URL = '/ai.vrm'; const VRMA_URL = '/fly.vrma'; const EARTH_RADIUS = 6378137; -// ★追加: 時刻を「2024年6月21日 12:00 (正午)」に固定するためのDateオブジェクト -// 季節によって太陽高度が変わるため、夏至の昼を選んでおくと最も明るくなります。 -const FIXED_DATE = new Date('2024-06-21T12:00:00'); +// 天気変更の間隔 (ms) - 5分 +const WEATHER_INTERVAL = 5 * 60 * 1000; + +// 時間の進行速度 (倍率) +const TIME_SCALE = 100; + +// 初期時刻: 正午 (12:00) +const INITIAL_DATE = new Date('2024-06-21T12:00:00'); + +// --- Weather Presets (天候設定) --- +const WEATHER_PRESETS = [ + { + name: 'Clear (快晴)', + coverage: 0.1, + layers: [ + { channel: 'r', altitude: 1500, height: 500, densityScale: 0.0 }, + { channel: 'g', altitude: 2500, height: 800, densityScale: 0.0 }, + { channel: 'b', altitude: 7500, height: 500, densityScale: 0.1 }, + ] + }, + { + name: 'Sunny (晴れ)', + coverage: 0.4, + layers: [ + { channel: 'r', altitude: 1500, height: 500, densityScale: 0.4 }, + { channel: 'g', altitude: 2500, height: 800, densityScale: 0.0 }, + { channel: 'b', altitude: 7500, height: 500, densityScale: 0.2 }, + ] + }, + { + name: 'Cloudy (曇り)', + coverage: 0.75, + layers: [ + { channel: 'r', altitude: 1500, height: 500, densityScale: 0.6 }, + { channel: 'g', altitude: 2000, height: 1000, densityScale: 0.5 }, + { channel: 'b', altitude: 7500, height: 500, densityScale: 0.0 }, + ] + } +]; // --------------------------------------------------------- -// Scene 1: Atmosphere (奥・リアルスケール・HDR設定) +// Scene Components (Inside Canvas) // --------------------------------------------------------- -// 明るさ調整用のコンポーネント -function ExposureController() { +// Canvasの内側で動作するメインシーンコンポーネント +function AtmosphereScene() { const { gl } = useThree(); + const sunRef = useRef(); + const atmosphereRef = useRef(); + const dateRef = useRef(new Date(INITIAL_DATE)); + const [weather, setWeather] = useState(WEATHER_PRESETS[1]); + + // 1. 露出設定 (Canvas内なのでuseThreeが使える) useEffect(() => { - // Takramのライブラリは物理単位の光量を扱うため、 - // 露出を劇的に上げる必要があります。 gl.toneMapping = THREE.NoToneMapping; gl.toneMappingExposure = 10.0; }, [gl]); - return null; -} -function AtmosphereLayer() { - const cameraRef = useRef(); + // 2. 天候の定期変更 + useEffect(() => { + const interval = setInterval(() => { + setWeather(prev => { + const others = WEATHER_PRESETS.filter(w => w.name !== prev.name); + const next = others[Math.floor(Math.random() * others.length)]; + console.log(`[Weather] Changing to: ${next.name}`); + return next; + }); + }, WEATHER_INTERVAL); + return () => clearInterval(interval); + }, []); + + // 3. 時間進行と太陽移動 (Canvas内なのでuseFrameが使える) + useFrame((state, delta) => { + // 時間を進める + const currentDate = dateRef.current; + const elapsedMs = delta * TIME_SCALE * 1000; + currentDate.setTime(currentDate.getTime() + elapsedMs); + + // Atmosphere更新 + if (atmosphereRef.current) { + atmosphereRef.current.updateByDate(currentDate); + } + + // 太陽移動 + if (sunRef.current) { + const hours = currentDate.getHours() + currentDate.getMinutes() / 60 + currentDate.getSeconds() / 3600; + const sunAngle = MathUtils.mapLinear(hours, 6, 18, 0, Math.PI); + const sunX = -Math.cos(sunAngle); + const sunY = Math.sin(sunAngle); + + if (hours < 6 || hours > 18) { + sunRef.current.position.set(0, -1, 0); + sunRef.current.intensity = 0.0; + } else { + sunRef.current.position.set(sunX, sunY, 0.2); + sunRef.current.intensity = MathUtils.lerp(0.5, 3.0, sunY); + } + } + }); return ( - - - + <> - {/* 太陽: 真上(正午) + 強度アップ */} - {/* ★修正: date={FIXED_DATE} を渡して時間を固定 */} - + - - - - + + {weather.layers.map((layer, index) => ( + + ))} - - - {/* 重要: AGXトーンマッピングで、高い露出でも色を綺麗にまとめる */} - + ); } @@ -117,7 +180,7 @@ function FlyOverCamera() { } // --------------------------------------------------------- -// Scene 2: Avatar (手前・ローカルスケール) +// Scene 2: Avatar // --------------------------------------------------------- function VrmCharacter() { const mixerRef = useRef(null); @@ -158,7 +221,6 @@ function AvatarLayer() { - {/* キャラのライティングは背景とは独立して、キャラが映える設定にする */} @@ -175,6 +237,15 @@ function AvatarLayer() { // --------------------------------------------------------- // Main App // --------------------------------------------------------- +function AtmosphereLayer() { + // Canvasが親となり、その中にロジックコンポーネント(AtmosphereScene)を入れる + return ( + + + + ); +} + export default function App() { const layerStyle = { position: 'absolute', @@ -187,7 +258,7 @@ export default function App() { return (
- {/* Layer 0: Atmosphere (明るさ補正済み) */} + {/* Layer 0: Atmosphere */}