Skip to content

Commit 1395058

Browse files
committed
fix(3d): 深色模式 3D 场景不渲染修复——双 EffectComposer 渲染抢占收敛与场景过渡重写
- 移除 DeepSeaWorld/AuroraDomeWorld 各自挂载的 SafeEffectComposer,后处理统一收敛至 SceneTransition 并暂禁用(render-target 兼容待排查) - SceneTransition 过渡逻辑重写:useEffect 驱动 + useFrame 外延迟同步可见性,避免 React 重渲染干扰 R3F 循环 - SceneProvider 新增 ThemeAwareEnvironment 主题感知雾效/背景;LoopResumer 改 useLayoutEffect + setTimeout 兜底唤醒渲染循环 - 新增程序化纹理 proceduralTextures,保留 ModuleEntity 纹理并清理调试日志 - 新增 bug 知识卡片:2026-08-dark-mode-dual-effectcomposer-render-takeover
1 parent 226259a commit 1395058

10 files changed

Lines changed: 1171 additions & 284 deletions

‎client/src/lib/3d/core/SceneProvider.tsx‎

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* @ai-context: 3D 场景核心(R3F):SceneProvider。
2222
*/
2323
import { Canvas, useThree } from '@react-three/fiber';
24-
import { Suspense, useEffect } from 'react';
24+
import { Suspense, useEffect, useLayoutEffect } from 'react';
2525
import { Preload } from '@react-three/drei';
2626
import { PerformanceMonitor } from './PerformanceMonitor';
2727
import { QualityController } from './QualityController';
@@ -30,6 +30,7 @@ import { ContextRecovery } from './ContextRecovery';
3030
import { useOrbitalStore } from '../navigation/OrbitalStore';
3131
import { usePerformanceModeStore } from '@/lib/performance/usePerformanceMode';
3232
import { PERFORMANCE_MODE_CONFIG } from '@/lib/performance/performanceMode';
33+
import { useSceneTheme } from '../hooks/useSceneTheme';
3334
import * as THREE from 'three';
3435

3536
interface SceneProviderProps {
@@ -39,23 +40,54 @@ interface SceneProviderProps {
3940
}
4041

4142
/**
42-
* 渲染循环唤醒器 — 修复“多次切换功能后页面无渲染”
43+
* 主题感知雾效/背景 — 深色用深蓝雾,浅色用浅蓝雾
44+
* 确保主题切换时雾色和背景色同步更新
45+
*/
46+
function ThemeAwareEnvironment() {
47+
const { scene } = useThree();
48+
const theme = useSceneTheme();
49+
50+
useEffect(() => {
51+
if (theme === 'deep-sea') {
52+
scene.fog = new THREE.FogExp2('#0a0a2e', 0.03);
53+
scene.background = new THREE.Color('#0a0a2e');
54+
} else {
55+
scene.fog = new THREE.FogExp2('#e8f4f8', 0.015);
56+
scene.background = new THREE.Color('#e8f4f8');
57+
}
58+
}, [theme, scene]);
59+
60+
return null;
61+
}
62+
63+
/**
64+
* 渲染循环唤醒器 — 修复"多次切换功能后页面无渲染"
4365
*
4466
* R3F 的循环在 frameloop='never' 下会 cancelAnimationFrame 彻底停止;
4567
* 切回 'always' 时 setFrameloop 只改 store 值,不会重启循环,
4668
* 而期间所有 invalidate() 又因 frameloop 尚为 'never' 被早退丢弃。
4769
* 因此在相位/档位迁移后(此时 frameloop prop 已应用)显式 invalidate:
4870
* 'always' 下 render 每帧返回 1,一次唤醒即可让循环永续运行;
4971
* docked('never')时 invalidate 自动早退,无副作用。
72+
*
73+
* 修复:使用 useLayoutEffect(同步执行,确保 frameloop prop 已挂载)
74+
* + setTimeout 兜底:React 18 中 useEffect 为异步微任务,frameloop
75+
* prop 可能在同一个微任务中尚未被 R3F 内部处理,导致 invalidate()
76+
* 因 frameloop 仍为 'never' 被早退丢弃。
5077
*/
5178
function LoopResumer() {
5279
const invalidate = useThree((s) => s.invalidate);
5380
const phase = useOrbitalStore((s) => s.phase);
5481
const mode = usePerformanceModeStore((s) => s.mode);
82+
const theme = useSceneTheme();
5583

56-
useEffect(() => {
84+
useLayoutEffect(() => {
85+
// 第一轮:同步执行,确保 frameloop 已挂载
5786
invalidate();
58-
}, [phase, mode, invalidate]);
87+
// 第二轮:setTimeout 兜底,确保 React 事务完成、R3F 已处理 frameloop 变更
88+
const timer = setTimeout(() => invalidate(), 0);
89+
return () => clearTimeout(timer);
90+
}, [phase, mode, theme, invalidate]);
5991

6092
return null;
6193
}
@@ -98,7 +130,7 @@ export function SceneProvider({ children, interactive = false }: SceneProviderPr
98130
gl.toneMappingExposure = 1.0;
99131
gl.shadowMap.enabled = true;
100132
gl.shadowMap.type = THREE.PCFSoftShadowMap;
101-
scene.fog = new THREE.FogExp2('#0a0a2e', 0.03);
133+
// 注意:fog 和 background 由 ThemeAwareEnvironment 组件管理(主题感知)
102134
// GPU 诊断:打印实际渲染器名称,出现 SwiftShader 即说明落入软件渲染(硬件加速失效)
103135
const ctx = gl.getContext();
104136
const dbgExt = ctx.getExtension('WEBGL_debug_renderer_info');
@@ -111,6 +143,7 @@ export function SceneProvider({ children, interactive = false }: SceneProviderPr
111143
{/* FPS 自动降档仅在概览态测量(entering 飞行帧率不代表设备能力,docked 无帧率可言) */}
112144
{phase === 'overview' && <PerformanceMonitor />}
113145
<LoopResumer />
146+
<ThemeAwareEnvironment />
114147
<QualityController />
115148
<MemoryManager />
116149
<ContextRecovery />

‎client/src/lib/3d/hooks/useSceneTheme.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ import { useState, useEffect } from 'react';
77

88
export type SceneTheme = 'deep-sea' | 'aurora-dome';
99

10+
function readTheme(): SceneTheme {
11+
const el = document.documentElement;
12+
return el.getAttribute('data-theme') === 'dark' ? 'deep-sea' : 'aurora-dome';
13+
}
14+
1015
export function useSceneTheme(): SceneTheme {
11-
const [theme, setTheme] = useState<SceneTheme>(() => {
12-
const el = document.documentElement;
13-
return el.getAttribute('data-theme') === 'dark' ? 'deep-sea' : 'aurora-dome';
14-
});
16+
const [theme, setTheme] = useState<SceneTheme>(() => readTheme());
1517

1618
useEffect(() => {
1719
const observer = new MutationObserver(() => {
18-
const el = document.documentElement;
19-
const isDark = el.getAttribute('data-theme') === 'dark';
20-
setTheme(isDark ? 'deep-sea' : 'aurora-dome');
20+
setTheme(readTheme());
2121
});
2222

2323
observer.observe(document.documentElement, {

0 commit comments

Comments
 (0)