使用 Flutter GPU 和 flutter_scene 构建 3D 酒店漫游与海岛场景后,我总结了自己的实践经验,包括各种 3D 视觉效果在手机上究竟需要付出多大的性能代价。

每一款应用都希望拥有出色的视觉体验。
解决用户的实际问题是第一要务,提供流畅的用户体验则是第二要务。如今,大多数开发团队都已经能够做到这两点。优秀的架构、成熟的用户体验设计模式,以及高质量的开发库,几乎所有团队都可以使用。
那么,究竟是什么让用户选择你的应用,并且愿意一次又一次地打开它呢?
对许多团队来说,答案是精致的视觉体验:漂亮的 UI、令人愉悦的动画,有时甚至可以更进一步,引入 3D 效果。
为什么 3D 在日常应用中仍然很少见?
多年来,人们一直认为 3D 技术即将在商业应用中普及。各家公司也不断尝试,但在我们日常使用的应用中,真正采用 3D 的产品仍然不多。
我认为主要有两个原因:
- 3D 并没有真正解决用户的问题。 3D 产品展示可能很酷,但用户真正想做的只是预订酒店、支付账单或者点一份外卖。
- 3D 的成本太高。 开发 3D 功能需要不同的专业技能、工具,以及更多测试工作。同时,它还可能增加应用体积,降低运行速度。
这也是为什么 3D 主要出现在游戏中。
人们玩游戏是为了获得娱乐体验。乐趣本身就是游戏的核心价值,因此 3D 带来的开发和性能成本是值得的。
但普通应用恰恰相反。用户希望尽快解决问题,然后继续自己的生活。他们并不想在你的应用里花费太多时间。
折中方案:适量使用 3D,但不影响正常操作
如果 3D 能够增加使用乐趣,同时又不会拖慢用户完成任务的速度呢?
例如,让用户在预订酒店之前,通过 3D 场景快速查看房间,同时仍然可以通过几次点击完成预订。
这样的 3D 功能就有可能真正融入商业应用。而这正是我想尝试的方向。
提前透露一下:我制作了两个演示项目,但最终只有一个通过了这个测试。具体原因会在文章最后揭晓。
Flutter 图形技术的发展历程
Flutter 一直以流畅的 UI 和动画体验著称。
2024 年 8 月,随着 Flutter 3.24 发布,Flutter 团队首次公开预览了 Flutter GPU。
这是一个底层图形 API,允许开发者直接使用 Dart 和 GLSL 编写自己的渲染器,而不需要编写任何原生平台代码。
Flutter 3.24 有哪些新功能?
Flutter GPU、多视图嵌入及更多新特性
当时,Flutter GPU 仅在 main channel 中提供,并且只支持 iOS、macOS 和 Android。
同一天,Brandon DeRosier 发布了一篇名为 Getting started with Flutter GPU 的文章,介绍了基于 Flutter GPU 构建的 3D 库 Flutter Scene (package:flutter_scene)。
有趣的是,Flutter 早在 2023 年 1 月的 Flutter Forward 大会上就首次展示了 3D 功能,但直到 2024 年才正式向公众开放预览。
此后,从外部来看,Flutter GPU 的开发似乎逐渐沉寂下来。
Flutter 团队最受关注的工作转向了另一个方向:将 Material 和 Cupertino 设计库 从 Flutter 框架中拆分出来,使其成为独立的软件包(迁移指南)。
这让我有些失望。
我一直希望通过 3D 技术让我们的应用更加惊艳,但我也理解,像这样的相对小众功能,很难成为团队的最高优先级。
然后,Flutter Scene 开始快速发展
这一切背后的关键人物是 Brandon DeRosier。
根据项目自己的介绍,Flutter Scene:
由一位前 Flutter 引擎核心团队成员开发。他曾在 Flutter 团队工作四年,其中大部分时间都在开发 Impeller 和 Flutter GPU。
实际上,Flutter Scene 最初是 Flutter 引擎内部为 Impeller 开发的一个 C++ 模块。
而 Flutter GPU 最初的开发目的,就是为了让这个项目能够脱离 Flutter 引擎,以普通 Dart 软件包的形式独立存在。
如今,Brandon 已经离开 Flutter 团队,独立维护这个项目,因此开发进展非常迅速。
截至本文撰写时,flutter_scene 已更新至 0.23.0,支持以下平台:
- iOS
- Android
- macOS
- Windows
- Linux
- Web
Brandon 已经展示过它在 Flutter 3.47 稳定版上的运行效果,而且所有平台都不需要修改 Flutter 源码或维护分支、不需要使用 Platform Views、不需要引入外部渲染器,也不需要创建第二套图形上下文。
他甚至还在使用 Flutter 开发一款 Scene Editor(场景编辑器)!
这里有几个容易混淆的细节,需要特别说明:
- Flutter 3.47 已经是稳定版本: Flutter Scene 可以在这个版本上运行。
- Flutter GPU 本身仍然需要主动启用: 可以通过
--enable-flutter-gpu参数启用,也可以在各平台配置中永久开启。iOS 和 macOS 使用Info.plist中的FLTEnableFlutterGPU,Android 则通过<meta-data>配置。 - Flutter 3.47 的变化: Flutter GPU 所依赖的 Impeller,现在已经成为 macOS、Windows 和 Linux 的默认渲染器。
- 相关工作仍在持续推进: Flutter 3.47.1 的热修复版本,让 Flutter GPU 可以在 Windows 和 Linux 的正式发布构建中使用。
Fluorite:另一个值得关注的 Flutter 3D 引擎
这是另一个让我非常期待的项目。
2026 年,在比利时布鲁塞尔举行的 FOSDEM 大会上,Toyota Connected North America 展示了 Fluorite。
这是一款基于 Flutter 和 Dart 构建、号称具有"主机游戏级"能力的游戏引擎。
它主要面向汽车数字座舱,使用 Google Filament 进行渲染。Filament 是一款开源的物理渲染引擎。
Toyota 之所以选择自主开发引擎,是为了避免 Unity 和 Unreal 带来的资源占用、复杂性及授权成本,同时也希望避免 Godot 的启动开销。
不过,自从项目公布以来,几乎没有更多消息。
官网仍然显示"更多内容即将推出......"。
我猜测,我们可能会在今年晚些时候看到它正式发布。
3D 基础知识:从低成本到极高成本的渲染技术
在讨论性能数据之前,我们先了解一下现代 3D 画面由哪些技术组成。
下面的性能成本分级,主要参考桌面和主机游戏中的常见情况。
如果是在手机上,几乎所有技术的成本等级都应该上调一级。
即使我们的酒店场景使用的"Ultra"画质主要由低成本和中等成本的效果组成,在 iPhone 17 Pro Max 上也只能达到 10~25 FPS。
接下来,我会逐一介绍这些技术,并说明 flutter_scene 0.23.0 是否支持它们。
低成本:每个 3D 应用都需要的基础能力

PBR 材质(Physically Based Rendering,基于物理的渲染)
- 作用: 通过基础颜色、金属度、粗糙度和法线贴图等物理属性描述物体表面。这样,即使不模拟真实的光线反弹,也能让金属看起来具有反射效果,让木材呈现哑光质感。
- flutter_scene 支持情况: 支持,通过
PhysicallyBasedMaterial实现。不过,清漆(Clearcoat)、光泽(Sheen)和各向异性(Anisotropy)等额外的物理材质层会启用计算成本更高的着色器,因此最好不要在地板、墙壁等大面积表面上启用这些效果。

阴影贴图(Shadow Maps,太阳光使用级联阴影)
- 作用: 从光源视角渲染一张深度图,用来判断哪些区域处于阴影之中,避免物体看起来像漂浮在空中。对于太阳这样的方向光,通常使用级联阴影贴图(Cascaded Shadow Maps):通过多张阴影贴图,让近处的阴影更加清晰,远处的阴影则使用较低精度。聚光灯通常只需要一张阴影贴图,而点光源需要使用包含六个面的立方体阴影贴图。
- flutter_scene 支持情况: 支持太阳光的级联阴影,并且可以选择启用 PCSS 柔和阴影。聚光灯也可以投射阴影,但点光源和区域光目前不支持投射阴影。

后期处理与色彩分级(Tone Mapping、Bloom、Color LUT)
- 作用: 这些效果会在完整的二维画面渲染完成之后执行。色调映射负责将高动态范围(HDR)的光照信息转换为屏幕能够显示的范围。泛光(Bloom)用于模拟高亮物体周围的发光效果。色彩查找表(LUT)则用于统一画面的色调和氛围。
- flutter_scene 支持情况: 支持 PBR Neutral、ACES、AgX、Reinhard 等色调映射算法,同时支持带镜头光晕的 Bloom,以及基于 LUT 的色彩分级。在我们的测试中,Bloom 几乎没有产生可测量的性能开销。

贴花(Decals)
- 作用: 在现有物体表面叠加划痕、污渍、水坑、灰尘等细节,不需要增加额外的多边形,也不需要为每个细节制作独立纹理。
- flutter_scene 支持情况: 目前还没有贴花系统。
有些技术列表会把它与**虚拟纹理(Virtual Texturing)**放在一起,但两者实际上完全不同。
虚拟纹理是一套纹理流式加载系统,可以根据需要将大型纹理载入或移出内存,同时也会产生额外开销。flutter_scene 目前同样不支持虚拟纹理。
中等成本:大多数惊艳视觉效果的来源

屏幕空间环境光遮蔽(SSAO / GTAO)
- 作用: 利用当前屏幕上已有的像素信息,让物体接触处和缝隙变暗,例如沙发腿与地板的接触位置、窗帘的褶皱等。这些细微的阴影可以明显增强场景的立体感。
- flutter_scene 支持情况: 支持 SSAO,也支持 GTAO(Ground Truth Ambient Occlusion)。在我们的测试中,GTAO 大约带来了 7% 的性能开销。

屏幕空间反射(Screen-space Reflections,SSR)
- 作用: 为地板、玻璃和水面生成反射效果,但只能反射当前屏幕中已经可见的物体。例如,当你把镜头向下移动,让被反射的物体离开屏幕时,对应的反射也会消失。
- flutter_scene 支持情况: 支持。在我们的测试中,SSR 大约增加了 10% 的性能开销 。此外,它还支持平面反射(Planar Reflections),适用于平面镜子和水面。这种技术会从镜子的视角重新渲染一次场景。

区域光(Area Lights,LTC)
- 作用: 区域光不是从一个点发光,而是从一个具有面积的形状发光,例如窗户、电视屏幕或灯光面板。它使用 LTC(Linearly Transformed Cosines,线性变换余弦)技术,产生柔和且具有形状特征的光照和高光。需要注意的是,柔和的光照不等于柔和的阴影。要生成柔和阴影,还需要额外的渲染技术。
- flutter_scene 支持情况: 支持基于 LTC 的
RectAreaLight,但不支持区域光阴影。在我们的测试中,它每帧大约增加 5 毫秒的渲染时间。

体积雾与丁达尔光(Volumetric Fog and God Rays)
- 作用: 模拟空气中的灰尘、薄雾或烟雾,以及阳光穿过窗户、树林时形成的可见光束。
- flutter_scene 支持情况: 两者都支持,通常属于中等到高成本效果。不过,在我们的场景中,它们几乎没有产生性能开销,但视觉效果也非常有限。例如,God Rays 在酒店房间中最多只改变了约 2% 的像素。

时间抗锯齿与超分辨率(TAA、DLSS、FSR、TSR)
- 作用: 先以较低分辨率渲染画面,再结合历史帧和运动向量重建清晰图像。DLSS 和 FSR 4 使用神经网络技术,而 FSR 2、FSR 3 和 Unreal Engine 的 TSR 则不依赖神经网络。这些技术的最终目的都是减少 GPU 的渲染负担。
- flutter_scene 支持情况: 支持 TAA 和渲染分辨率缩放,但不支持这些厂商提供的超分辨率技术。仅仅将渲染比例降低到 85% ,我们就减少了 21%~29% 的单帧渲染时间,而且肉眼几乎看不出区别。不过,TAA 会让快速移动的细小物体变得模糊,例如雨滴形成的细长轨迹。
高成本:主要适用于高端 GPU

全局光照(Global Illumination,GI)
- 作用: 模拟光线在不同物体表面之间的反弹。例如,阳光照射在红色墙壁上,反射光会让旁边的白色地板略微泛红。这种现象被称为颜色溢出(Color Bleeding)。全局光照还可以让没有直接光源照射的房间不至于完全漆黑。
- flutter_scene 支持情况: 支持光照探针(Light Probes),可以预先捕获一次场景光照。如果场景没有变化,后续几乎不产生额外开销。此外,还支持一种简化版的实时全局光照方案,可以理解为"轻量版 Lumen"的动态探针网格。但它的性能开销非常高,在我们的测试中最多占用了整帧渲染时间的 55% ,而且只能利用摄像机已经看到过的场景信息。对于普通应用来说,这种成本完全不值得。
硬件光线追踪(Hardware Ray Tracing)
- 作用: 通过在场景中追踪真实光线,计算反射和阴影。与屏幕空间技术不同,光线追踪可以考虑摄像机视野之外的物体。
- flutter_scene 支持情况: 目前不支持。虽然部分手机已经配备了光线追踪硬件,但 Flutter GPU 目前还没有提供光线追踪 API 或计算着色器(Compute Shaders)。

次表面散射(Subsurface Scattering,SSS)
- 作用: 模拟光线进入半透明材质,在内部发生散射,最后从其他位置射出的过程。这种效果对于真实呈现皮肤、蜡、大理石和玉石等材质非常重要。例如,当阳光从背后照射人的耳朵时,耳朵会呈现半透明的红色,这就是典型的次表面散射效果。
- flutter_scene 支持情况: 目前不支持。它虽然支持 PBR 透射(Transmission),可以模拟玻璃等薄型透明材质,但这与次表面散射是两种不同的效果。

虚拟化高密度几何体(Nanite、Mesh Shaders)
- 作用: 每帧动态加载和绘制数百万个微小三角形。这样,即使使用电影级扫描模型,也几乎看不到多边形边缘。
- flutter_scene 支持情况: 目前不支持,因为 Flutter GPU 没有提供 Mesh Shader。不过,它提供了更适合移动设备的替代方案:细节层次(Level of Detail,LOD) 。通过
LodComponent,系统可以根据物体与摄像机之间的距离,自动切换不同复杂度的模型。
极高成本:主要用于电影和顶级 PC

完整路径追踪(Full Path Tracing)
- 作用: 通过模拟大量光线在场景中的多次反弹,计算光照、阴影、反射和折射,从而生成尽可能接近真实世界的画面。即使在性能最强的 PC 上,实时路径追踪通常也需要结合降噪和超分辨率技术才能达到可用的性能。

焦散(Caustics)
- 作用: 模拟光线穿过水或玻璃后发生聚焦,在物体表面形成明亮且不断变化的光斑。例如,游泳池底部那些随水波不断晃动的光纹。真实计算这种效果的成本非常高。
- 实际应用: 我们的海岛场景并没有真正计算焦散,而是在地面着色器中模拟了这种效果。具体做法是使用两层预先烘焙好的光纹贴图,让它们在海床表面缓慢移动。整个效果只需要两次纹理读取。

流体、火焰与烟雾模拟(Fluid, Fire and Smoke Simulation)
- 作用: 在三维网格中求解流体方程,模拟液体、火焰和烟雾的运动。模拟结果通常会保存为 OpenVDB 等体积数据,并结合光线在介质内部的多次散射进行渲染。
- 实际应用: 游戏和普通应用通常使用粒子系统来替代真实的流体模拟。
flutter_scene提供了基于 CPU 的粒子系统。在我们的海岛场景中,每秒会生成约 2300 条雨滴轨迹,雨滴可以与场景中的物体发生碰撞。营火同样使用粒子系统实现。

基于发丝的毛发渲染(Strand-based Hair and Fur)
- 作用: 模拟数万根独立的毛发,每根毛发都拥有自己的阴影和高光。与使用平面纹理卡片模拟头发相比,这种方式更加真实。
- flutter_scene 支持情况: 目前不支持。不过,对于普通应用来说,你大概率也用不到它。
这一部分的结论是:
普通应用只需要使用低成本的基础渲染技术,再根据实际需要加入一两个中等成本的效果,就已经能够获得非常不错的视觉表现。
至于高成本和极高成本的技术,还是更适合游戏和电影制作。
实际测试:这些效果在手机上究竟有多大开销?
我非常喜欢 3D,因此专门制作了两个演示项目,想看看在真实手机上,Flutter 3D 究竟能做到什么程度。


- 酒店演示(Home Demo): 一个支持自由漫游的 3D 酒店场景,包含位于 30 楼的两间相连家庭客房、海景、昼夜切换、下雨和闪电效果、可以操作的灯具和窗帘,以及房门旁的"立即预订"按钮。
- 海岛演示(Island Demo): 一个小型 3D 海岛,包含海水、棕榈树和营火,并提供三种不同的画质模式。
两个演示都集成在一个普通的三标签页 Flutter 应用中,与常规首页并列存在。
所有性能测试均在 iPhone 17 Pro Max 上进行,使用 Profile 构建模式,Flutter 版本为 3.47.2 ,flutter_scene 版本为 0.23.0。
经验一:先测量性能瓶颈,因为问题很可能出在 GPU
一开始,Flutter 的帧性能图表显示,酒店场景的 UI 线程每帧耗时达到 26~105 毫秒。
所以我最初认为是 Dart 代码执行得太慢。
但当我使用 Dart VM 的 CPU Profiler 对 UI 线程进行采样后,发现了一个意外的结果:
UI 线程有 87%~89% 的时间都在等待。
具体来说,它阻塞在 Metal 的 CreateCommandBuffer 调用中,因为 GPU 还在处理之前提交的渲染帧。
flutter_scene 会在 UI 线程中记录并提交每一个渲染 Pass。
当 GPU 的处理速度跟不上时,UI 线程也会被迫等待。
因此,在这种情况下:
看起来是 UI 线程耗时过长,实际上真正的瓶颈却是 GPU。
解决方案自然也应该针对 GPU:减少需要渲染的像素、减少渲染 Pass,以及减少数据上传。
经验二:最有效的两个优化手段是降低分辨率和关闭动态全局光照
下面的大部分测试数据,都是在相同摄像机位置下,以完整的 Ultra 画质为基准,逐项开启或关闭某个效果进行对比得到的。
其余数据则来自截图分析和引擎源码。
以下是我测试过的全部技术:
| 技术 | 作用 | 在 iPhone 上的性能开销 | 普通应用是否值得使用 |
|---|---|---|---|
| 渲染比例(Render Scale) | 以较低分辨率渲染 3D,再将画面放大 | 85% 时减少 21%~29% 的帧耗时;75% 时减少 47%~49% | 值得。85% 在 3× 屏幕上几乎看不出区别 |
| 实例化渲染(Instancing) | 一次性绘制同一个网格的多个副本 | 700 个物体从 700 次绘制调用减少到 2 次 | 值得,适合重复物体 |
| PBR 光照与天空 | 使用天空光照亮真实感材质 | 基础渲染成本 | 值得,但需要调整曝光。物理天空在白天需要 0.35 的曝光值,黄昏时还曾因公式问题变黑 |
| 太阳阴影 | 让物体投射阴影 | 属于基础成本;缓存静态物体阴影在主要视角下没有明显收益 | 值得,但需要降低阴影区域的环境光亮度,否则阴影几乎看不出来 |
| 带阴影的聚光灯 | 让灯具能够投射阴影 | 每帧增加约 3 ms;同时开启四盏灯时,阴影纹理容量被耗尽,导致隔壁房间变黑 | 有限使用,只适合摄像机附近的灯光 |
| 区域光 | 模拟窗户或电视屏幕发出的柔和光线 | 每帧增加约 5 ms,但视觉效果比较细微 | 通常不值得 |
| 光照探针 | 预先捕获房间光照并重复使用 | 场景不变时几乎没有额外开销 | 值得 |
| 动态全局光照 | 实时更新墙面等物体之间的反射光照 | 某个位置最多占整帧时间的 55%,其他位置约为 5%~28%;而且只能利用摄像机已经看到过的信息 | 不值得,额外的真实感不足以弥补帧率下降 |
| 屏幕空间反射 | 反射屏幕上可见的物体 | 约 10% | 视情况而定 |
| 环境光遮蔽 | 让物体接触处产生柔和的暗部 | 约 7% | 视情况而定 |
| Bloom 泛光 | 在明亮物体周围产生光晕 | 没有可测量的开销 | 值得,但需要结合曝光正确设置阈值,否则整个画面都会泛光 |
| MSAA 与 SMAA/FXAA | 平滑物体边缘的锯齿 | 酒店场景中差异在 ±5% 以内;海岛场景中的 MSAA 开销较高,因为水面需要读取已渲染的场景 | 不值得刻意切换,肉眼几乎看不出区别 |
| God Rays 光束 | 产生可见的光束 | 在朝南的酒店房间中最多改变 5%~7% 的像素 | 通常不值得,除非场景专门为光束设计 |
| 体积雾 | 产生带有发光效果的高度雾 | 几乎没有额外开销;最初版本看起来与普通雾几乎没有区别 | 调整难度较高,需要仔细调参 |
| 平面反射(镜子、海面) | 从反射面的视角重新渲染场景 | 每个反射面都需要额外渲染一次场景;将海面反射限制为远处物体后,开销几乎可以忽略 | 值得,前提是合理剔除物体 |
| 折射(玻璃上的雨水) | 扭曲玻璃后方已经渲染完成的画面 | 每个可见表面都需要复制一次完整画面 | 只在下雨时启用 |
最终,我把 Ultra 画质的渲染比例降低到 85%,同时关闭动态全局光照。
在 12 个不同的摄像机位置进行测试后,平均帧率提升到了原来的 1.41 倍 ,最高达到 1.72 倍。
除了失去部分间接反射光照之外,画面几乎没有肉眼可见的变化。
还有一个意外发现:
隐藏的 3D 标签页几乎不会消耗性能。
我让海岛场景以最高画质模式加载在后台标签页中,并通过 Flutter 的 TickerMode 关闭它的动画更新。
结果,普通首页仍然可以保持 120 FPS ,UI 线程耗时仅 0.4 ms。
当我再次切回海岛页面时,场景仍然保持在离开时的状态。
最终测试结果
海岛场景的简单画质模式可以达到 120 FPS。
最高画质模式则根据手机温度不同,运行在大约 37~60 FPS。
而酒店场景在完整 Ultra 画质下,只有 10~25 FPS。
经过这些测试,我最大的感受是:
Flutter 中的 3D 确实很酷,但它毕竟不是一款 AAA 级游戏引擎。只要保持场景简单,它就能运行得非常流畅。
另外,Flutter Scene 还在软件包中提供了专门面向 AI 编程助手的 Agent Skills。
这些技能涵盖常用开发模式和容易踩的坑、运行---等待稳定---截图验证的完整流程、现成的视觉效果预设、程序化内容生成,以及性能分析与优化。
可以通过以下命令安装:
dart run flutter_scene:init
该命令会提示是否安装这些技能。
如果只想安装或更新技能,也可以执行:
dart run flutter_scene:skills
我制作的两个演示项目,都是借助使用了这些技能的 AI 编程助手完成的。
其中,性能优化技能的第一条原则是:
先测量究竟是哪一个线程超出了性能预算。
而这条原则,恰恰帮助我发现了前面提到的第一个重要问题:看似是 Dart UI 线程性能不足,实际上真正拖慢整个应用的可能是 GPU。