当一个 3D 场景只服务于固定内置模型时,很多渲染参数会自然地围绕这批资源调出来。只要资源不变,这些参数看起来就是"系统默认值"。
但一旦开始支持用户上传自己的模型,问题就会变得不一样。不同来源的 GLB 可能有不同的法线方向、材质方式和贴图处理。原本针对内置模型成立的参数,到了另一类资源上可能直接失效。
我之前在一个 3D 场景 demo 里加了"上传自己的 GLB 模型"功能。场景原本已经有一套针对内置模型调好的双面渲染和灯光参数。用真实的房间扫描模型测试后,连续出现了两个问题:俯视时室内被一整块白色表面挡住;修完以后,模型贴图又整体过曝。
这两个问题最后指向的是同一件事:系统把针对固定资源成立的假设,当成了对所有上传资源都成立的规则。
根因诊断和具体修法主要来自 Claude Code,我负责用真实扫描模型验证修改后的表现,以及判断当前方案的适用范围。
为了提高兼容性,最初对上传模型统一开启了双面渲染。这个选择看起来很保险:即使某些模型法线方向不一致,也不容易出现某个面完全看不到的问题。
但房间扫描模型恰好暴露了这个假设的问题。
这类模型本质上更接近一个从室内观察的壳体,法线朝向内部并不是错误,而是符合它的使用方式。开启双面渲染以后,原本应该从外部不可见的背面也被画了出来。俯视模型时,天花板背面正好挡在室内上方,于是画面看起来像覆盖了一整块白色表面。
最终改回只渲染正面,让背面剔除恢复作用。这样从上方观察时,天花板背面不会继续遮住室内,扫描空间可以正常被看到。
这里真正需要调整的不是某个法线,而是原来的兼容策略:"把所有面都画出来"并不一定更稳妥,它会同时取消模型本来依赖的遮挡规则。
遮挡问题修好之后,扫描模型仍然明显偏白,贴图里的细节和明暗关系都被冲淡了。
场景原来的灯光是为纯色 3D 物体调的。这类对象需要环境中的灯光才能建立体积感,因此较强的场景灯光是合理的。
但上传的扫描模型不是同一种资源。它的贴图里已经包含了扫描时记录下来的明暗效果。继续让材质参与场景灯光计算,相当于在已经包含光照信息的贴图上再次叠加照明,最终表现就是整体过曝。
当前的处理方式,是让上传模型使用不受场景灯光影响的材质表现,同时保留原有贴图、顶点色和透明度等信息。这样扫描贴图自身的明暗关系可以直接显示,不再被场景灯光二次改变。
这个调整再次说明:场景灯光不是一个与资源无关的固定参数,它依赖模型材质本身是怎样表达明暗的。
这个修法并没有解决"任意 GLB 都能正确显示"这个更大的问题。
如果用户上传的是另一类普通 3D 模型,本身没有把光照烘进贴图,需要依赖场景灯光来表现体积,那么统一忽略灯光之后,它反而可能显得很平。
也就是说,目前的方案更适合这次测试中的扫描类模型,但它仍然是一种资源类型取舍,而不是通用答案。
如果后续真的需要同时覆盖两类模型,更合理的方向不是继续找一个"万能灯光参数",而是允许不同资源采用不同的渲染策略,例如提供"使用原始光照 / 忽略场景光照"一类的选择。
这部分目前还没有进一步实现,因此我更愿意把它明确记录成方案边界,而不是把这次修复描述成已经完成了完整的上传模型兼容。
这两个问题在内置模型阶段都不会暴露,因为原来的参数本来就是围绕那批资源调出来的。
真正加入用户上传以后,资源来源变得不可控,系统之前没有写出来的假设才开始显现:
因此,从固定内容扩展到用户输入时,不能只把"上传文件"当成新增了一个入口。更重要的是重新检查:原来的场景默认值里,有多少其实只是内置资源的特例。
这次两个具体根因和修法主要由 Claude Code 协助分析,我负责把不同类型的真实模型放回场景里验证,并确认修复以后新的取舍是什么。
对我来说,这次更值得保留的不是"双面渲染应该开还是关",也不是"上传模型要不要受灯光影响",因为这两个答案都会随资源类型变化。
更稳定的判断是:只要系统开始接受自己无法预先控制的输入,就需要重新审视那些原本看起来理所当然的默认参数。