Skip to content

反射的统一

背景:水面反射和地面反射用两套互斥机制,双双失效。ADR-092 决策统一为单一纹理工厂 + 单一平面反射引擎。 过程:定义 TextureFactory 类 → 统一 canvas/外部贴图创建 → 统一 RenderTargetTexture 反射 → 消除互斥逻辑。


七月十二日,晚上。

Riku 站在屏幕前,看着环境子系统的代码。

水面反射:RenderTargetTexture + 手写 _mirrorCam_worldMatrix 黑魔法)。

地面反射:MirrorTexture(靠 mirrorPlane 推导)。

两套机制,互斥——地面启用反射时销毁水面反射,但关闭地面反射时不恢复水面。

"两套反射,双双失效," Riku 对自己说。


问题

ADR-092 的文档打开着,标题是「贴图与反射统一 —— 单一纹理工厂 + 单一平面反射引擎」。

问题清单:

#问题证据
1贴图创建散点(11 处)无统一工厂,重复模式 + 泄漏风险
2地面 MirrorPlane 未经验证符号/朝向可能错误
3地面双重驱动引擎自动渲染 + 手动 .render() → 反射错乱
4水面互斥单向地面反射销毁水面反射,但关闭地面反射不恢复水面
5水面 _mirrorCam 脆弱主相机切换/FOV 变化时不更新

"11 处贴图创建," Jieling 看着屏幕说,"没有统一管理?"

"对," Riku 说,"每处都是 document.createElement('canvas') → getContext('2d') → draw → toDataURL → new Texture。重复模式,没有统一释放。"


纹理工厂

Riku 在 src/scene/env/ 下新建了 texture-factory.ts

typescript
class TextureFactory {
    private textures: Map<string, Texture>;
    
    constructor(scene: Scene) {
        this.scene = scene;
        this.textures = new Map();
    }
    
    createCanvasTexture(
        key: string,
        width: number,
        height: number,
        drawFn: (ctx: CanvasRenderingContext2D) => void
    ): Texture {
        // 复用已有纹理
        if (this.textures.has(key)) {
            return this.textures.get(key)!;
        }
        
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d')!;
        drawFn(ctx);
        
        const texture = new Texture(canvas.toDataURL(), this.scene);
        this.textures.set(key, texture);
        return texture;
    }
    
    dispose(key: string): void {
        const texture = this.textures.get(key);
        if (texture) {
            texture.dispose();
            this.textures.delete(key);
        }
    }
    
    disposeAll(): void {
        for (const texture of this.textures.values()) {
            texture.dispose();
        }
        this.textures.clear();
    }
}

"统一创建,统一释放," Riku 说,"不再有散点。"

"释放登记呢?" Jieling 问。

"在 disposeAll() 里," Riku 说,"场景销毁时调用 textureFactory.disposeAll(),确保所有纹理被释放。"


反射引擎

统一反射引擎更复杂。

Riku 的方案:水面和地面都用 RenderTargetTexture + 同一个 _mirrorCam

typescript
class PlanarReflectionEngine {
    private renderTarget: RenderTargetTexture;
    private mirrorCam: FreeCamera;
    
    constructor(scene: Scene, mirrorPlane: Plane) {
        this.mirrorCam = new FreeCamera('mirrorCam', Vector3.Zero(), scene);
        this.renderTarget = new RenderTargetTexture('reflection', 1024, scene);
        
        // 镜像相机设置
        this.mirrorCam.position = mirrorPlane.normal.scale(-1);
        this.mirrorCam.setTarget(Vector3.Zero());
    }
    
    update(mainCamera: Camera): void {
        // 同步主相机到镜像相机
        this.mirrorCam.position = mainCamera.position.clone();
        this.mirrorCam.setTarget(mainCamera.getTarget());
        
        // 镜像变换
        this.mirrorCam.position.y = -this.mirrorCam.position.y;
    }
    
    render(): void {
        this.renderTarget.render();
    }
}

"一套引擎," Riku 说,"水面和地面共用。不再互斥。"

"互斥逻辑删了?" Jieling 问。

"删了," Riku 说,"现在水面和地面可以同时有反射。"


尾声

Riku 提交了代码。

feat: 纹理工厂(texture-factory.ts)— 统一贴图创建与释放
feat: 平面反射引擎统一(ADR-092)— 水面/地面共用单一引擎

他站在窗边,看着远处的天际线。七月的阳光已经落下去了,路灯一盏一盏亮起来。

反射从两套变成了一套。互斥消失了,水面和地面可以同时闪耀。

"反射是城市的镜子," Riku 对自己说,"镜子不能互相打架。"


教训:两套互斥机制不是设计,是债务。统一为一套引擎,消除互斥逻辑,让水面和地面共存——这是最简单的方案,也是唯一正确的方案。