2026/10/10 8:41:04

Bevy 大型场景压力测试示例 bevy_city 深度解析:程序化城市生成、多级 LOD 与 ECS 性能优化实战

Bevy 大型场景压力测试示例 bevy_city 深度解析:程序化城市生成、多级 LOD 与 ECS 性能优化实战 游戏开发图形学【免费下载链接】bevyA refreshingly simple>项目地址https://gitcode.com/GitHub_Trending/be/bevy点击查看免费下载导读bevy_city是 Bevy 仓库examples/large_scenes目录下的一个大型场景示例工作区成员见 Cargo.toml它以 Kenney 开源素材包为基础程序化生成一座包含道路、车辆、建筑、树木、栅栏与步行小径的完整城市。该示例的定位并非普通的教学 Demo而是一个贴近真实游戏形态的大规模场景压力测试通过尽可能多地叠加 Bevy 的渲染与 ECS 特性让病态路径在开发阶段就暴露出来而不是等到最终用户手中。阅读本文后你将掌握该示例的运行方式与全部命令行参数、程序化城市的生成算法、基于VisibilityRange的两级 LOD 实现、运行时设置 UI以及静态场景优化、CPU 剔除等性能手段在实际代码中的落地方式。一、示例定位为什么需要一个城市级别的压力测试bevy_city的源码开篇注释main.rs明确说明了它的使命This scene is intended to be an attractive, fairly realistic stress test of Bevys capacity to model extremely large scenes. As a result, the complexity is higher than in most examples or benchmarks — we want to use a large number of features so that pathological paths are caught during development, rather than by end users.这意味着它与 benches 目录下的基准测试不同基准测试追求单一指标的微基准而bevy_city追求特征叠加的真实场景——同时启用阴影贴图、接触阴影、大气散射、Bloom、TAA、多级 LOD、CPU 剔除、静态变换优化等特性让它们在同一场景中相互交织从而暴露组合使用时的性能问题。其 README.md 还透露了项目灵感来源与演进方向该场景受 flecs-hub 的 traffic 演示项目启发目前尚未实现真正的交通仿真但作者的目标是未来用bevy_ecs实现与之类似的交通模拟。当前代码中已有一个占位性质的简单车辆移动系统后文详述为后续的 ECS 交通仿真预留了位置。二、运行方式与完整命令行参数2.1 依赖与特性开关该示例在 Cargo.toml 中以工作区成员形式存在运行命令为cargo run -p bevy_city --release注意其 Bevy 依赖启用了以下特性Cargo.toml特性用途https允许通过 HTTPS 加载远程素材城市素材托管在远程 bevy_asset_files 仓库的 kenney 目录下free_camera启用FreeCameraPlugin提供自由飞行相机控制bevy_feathers启用运行时设置面板Feathers UI 组件库web_asset_cache远程素材的本地缓存bevy_dev_tools提供merge_all_mesh_3d等开发辅助工具用于合并车辆网格外部依赖为argh命令行参数解析、noiseOpenSimplex 噪声生成城市密度分布、rand随机数。运行示例前请确保网络可访问远程素材服务器因为城市资产是通过AssetServer以 HTTP 方式流式加载的assets.rs中的BASE_URL常量指定了加载根路径。2.2 命令行参数来自Args结构体bevy_city使用argh从命令行解析参数完整参数定义位于 main.rscargo run -p bevy_city --release -- \ --seed 42 \ --size 30 \ --car-density 0.05 \ --lod-min-range 200.0 \ --lod-max-range 500.0 \ --car-lod-min-range 40.0 \ --car-lod-max-range 80.0 \ --no-cpu-culling参数类型默认值说明--seedu6442城市生成的随机种子同一种子生成完全相同的城市布局--sizeu3230城市网格的半边长块数实际生成size × size个城市街区--car-densityf320.05道路上的车辆密度取值范围0..1控制每个车位生成车辆的概率--no-cpu-culling开关关闭为所有网格添加NoCpuCulling组件跳过 CPU 剔除--lod-min-rangef32200.0建筑等大型物体 LOD 切换的最小距离--lod-max-rangef32500.0建筑等大型物体 LOD 切换的最大距离--car-lod-min-rangef3240.0车辆 LOD 切换的最小距离车辆更小可更激进--car-lod-max-rangef3280.0车辆 LOD 切换的最大距离这些 LOD 距离参数不仅参与可见性范围的计算还直接决定了 assets.rs 中VisibilityRange资源的构造——建筑使用lod_min_range/lod_max_range生成两档范围车辆使用独立的car_lod_min_range/car_lod_max_range。三、主流程与加载状态机3.1 App 初始化main.rs 中的App装配展示了本示例启用的插件组合窗口标题bevy_city分辨率1920x1080PresentMode::AutoNoVsync主显示器居中并强制缩放因子1.0插件DefaultPluginsFreeCameraPlugin自由相机FeathersPlugins设置 UIWireframePlugin线框调试资源黑色清屏色、WinitSettings::continuous()持续渲染不因无输入而休眠、Settings运行时配置、深色 UI 主题create_dark_theme()、默认关闭的全局线框配置静态场景优化.insert_resource(StaticTransformOptimizations::Enabled)——这是大场景的核心优化之一详见第六节。场景本身通过 BSN 场景图bsn_list!/bsn!宏声明式构建包含三棵子树camera相机、sun平行光、loading_screen加载界面。3.2 三阶段加载流程Message 驱动的状态机城市生成依赖大量远程资产因此加载被拆成三个由Message串联的阶段main.rsCityAssetsLoaded ──▶ CityAssetsReady ──▶ CitySpawnedCityAssetsLoadedupdate_loading_screen系统main.rs遍历CityAssets中的全部UntypedHandle逐个检查asset_server.is_loaded_with_dependencies在加载界面实时显示Loading assets: n/m与未加载路径列表。当全部加载完成后界面文字切换为Processing assets...并通过commands.write_message(CityAssetsLoaded)进入下一阶段。注释特别说明故意使用 Message 而非 Event确保资产处理从下一帧才开始。CityAssetsReadyprocess_assets系统main.rs执行merge_car_meshes网格合并详见第四节随后写入CityAssetsReady。CitySpawnedon_city_assets_ready系统main.rs调用spawn_city真正生成城市并以info!日志输出各类实体的统计数量车辆、道路、树木、建筑、栅栏、路径与总计。随后on_city_spawned系统销毁加载界面实体。四、程序化城市生成噪声驱动的街区布局4.1 城市街区网格与路面spawn_citygenerate_city.rs是生成入口。其布局规则在注释中以 ASCII 图说明X------- | B B B | B B B X crossroad, B buildings每个城市街区为5.5 单位 × 4.0 单位所有资产都相对十字路口crossroad位置生成。每个街区生成以下内容道路一个十字路口资产 水平/垂直两条拉伸后的直路资产详见下地面瓦片根据密度选择默认材质或草地材质覆盖整个街区密度区基于 OpenSimplex 噪声计算density按阈值划分森林、低密度、中密度、高密度四种区域。4.2 噪声驱动的密度分区let noise OpenSimplex::new(rng.random()); let noise_scale 0.025; // density 归一化到 0..1 let density noise.get([x * noise_scale, z * noise_scale, 0.0]) * 0.5 0.5;分区阈值定义在 generate_city.rs密度区间区域类型内容density 0.45森林随机稀疏地生成小树/大树 0.6低密度区4 栋低密度建筑 栅栏 两列小树 0.7中密度区10 栋中密度建筑 大树 步行小径 双排栅栏其余高密度区6 栋高密度建筑摩天楼各密度区的具体摆放建筑位置、旋转、树木行数、栅栏间距都有独立的生成函数数值多为手工微调以求视觉美观源码注释NOTE most of the magic numbers were hand tweaked。4.3 道路与车辆的生成技巧spawn_roads_and_carsgenerate_city.rs采用了一个值得借鉴的性能技巧不重复生成多段道路资产而是生成一段直路资产后通过旋转与缩放拉伸覆盖整个街区。水平道路以Scale(4.5, 1, 1)拉伸垂直道路以Scale(3, 1, 1) 绕 Y 轴旋转 90° 实现。道路同时携带Road { start, end }组件定义了车辆的行车线段。车辆以car_density概率在两个车道±0.15偏移上生成Car组件记录了offset、distance_traveled、dir三个字段供后续交通模拟使用。五、资产加载与网格优化5.1 资产体系CityAssets资源assets.rs 的CityAssets资源集中管理全部素材句柄并通过load_asset!宏自动将句柄登记进untyped_assets以跟踪加载状态从远程BASE_URL加载车辆15 种车型hatchback-sports、suv、sedan、truck、van、taxi、ambulance、police、firetruck 等以GltfAssetLabel::Scene(0)加载道路road-crossroad-path.glb与road-straight.glb建筑高密度city-kit-commercial 摩天楼 5 种 商业楼 2 种、中密度7 种、低密度city-kit-suburban 12 种每种密度的材质从对应纹理包colormap、variation-a/b/c随机组合其他地面瓦片、大小树木、栅栏、步行小径、树/车 LOD 盒体网格。5.2 车辆网格合并减少每帧处理的网格数merge_car_meshesassets.rs通过merge_all_mesh_3d把每个车辆 glTF 场景中的所有网格合并为单个网格。源码注释解释了动机Kenney 素材包中每个轮胎、部分车门都是独立网格这在需要逐部件动画时有用但本场景不需要多个网格意味着每帧要为每个网格运行变换传播transform propagation并生成更多间接命令indirect commands。合并后car_meshes每个车型只保留一个网格。5.3 程序化 LOD 网格PendingLod与uv_cube建筑与车辆使用PendingLod组件声明待解析的 LODassets.rs随后由resolve_pending_lods系统assets.rs在帧循环中解析读取源网格的 AABBsource.get_aabb()以 AABB 尺寸生成一个匹配的立方体 LOD 网格带缓存LocalHashMapHandleMesh, HandleMesh同一源网格只生成一次将立方体 LOD 插入实体、按 AABB 中心平移、移除PendingLod。立方体网格由uv_cube函数assets.rs手工构建——它是 Bevy 默认立方体的定制版本顶面与侧面使用可配置的不同 UV 矩形。注释说明Kenney 各素材包沿用同一套 UV 布局顶部对应屋顶主色、侧面对应墙体主色因此 LOD 立方体顶面采样屋顶色、侧面采样墙面色可以显著缓和 LOD 切换时的观感跳变。对应的 UV 常量low_density_lod_uv等在 assets.rs 中手工定义。六、多级 LODVisibilityRange的落地实践6.1 可见性范围组件语义城市中每个物体都以高模 LOD 低模两个子实体的形式存在通过VisibilityRange组件源自 crates/bevy_camera/src/visibility/range.rs控制切换。该组件包含三个字段start_margin: Rangef32相机拉远时淡入的距离区间首尾相同则不淡入、直接切换end_margin: Rangef32相机继续拉远时淡出的距离区间use_aabb: bool为 true 时以网格 AABB 中心作为距离计算基准通常保持 false保证各 LOD 位置一致以便平滑交叉淡入淡出。该模块文档给出的典型层级示例range.rs为高模[0,0)→[20,25)低模[20,25)→[70,75)billboard 替身[70,75)→[150,160)——上一档的end_margin必须与下一档的start_margin完全一致交叉淡入淡出才能正常工作。6.2 bevy_city 中的两档 LOD以建筑为例assets.rs每个建筑实体有两个子实体子实体 1完整建筑网格 visibility_ranges[0]近距离档子实体 2PendingLod生成的立方体 LOD visibility_ranges[1]远距离档。visibility_ranges的构造assets.rs以命令行参数为边界visibility_ranges: vec![ VisibilityRange { start_margin: 0.0..0.0, end_margin: args.lod_min_range - 1.0..args.lod_min_range 1.0, // 交叉淡出区间 use_aabb: false, }, VisibilityRange { start_margin: args.lod_min_range - 1.0..args.lod_min_range 1.0, // 与上一档一致 end_margin: args.lod_max_range..args.lod_max_range, // 超出即完全消失 use_aabb: false, }, ],可以看到end_margin与下一档start_margin均为lod_min_range ± 1.0严格遵循交叉淡入淡出契约。车辆使用更激进的独立参数car_lod_min_range40/car_lod_max_range80且 LOD 是硬编码的 1.0×1.0×2.5 立方体——因为车辆难以像建筑那样识别主色调。6.3 场景资产的可见性传播部分资产如树木、栅栏以WorldAssetRoot方式从世界资产WorldAsset实例化而非直接生成Mesh。因此main.rs注册了两个观察者observeradd_no_cpu_culling_on_scene_readymain.rs监听WorldInstanceReady事件该事件由世界资产实例化完成时触发定义于 crates/bevy_world_serialization/src/world_asset_spawner.rs遍历实例的所有后代为网格实体补插NoCpuCullingpropagate_visibility_rangemain.rs将实例根的VisibilityRange复制到每个网格后代——因为VisibilityRange不会自动向子实体传播源码文档明确说明需手动放置到每个参与 LOD 的实体上。七、朴素的车辆移动未来 ECS 交通仿真的占位实现simulate_cars系统main.rs实现了最基础的车辆移动车辆沿Road组件的start→end线段匀速移动速度固定1.5单位/秒到达终点后distance_traveled归零回到起点形成无限循环。源码注释坦诚地说明了当前状态与目标Do a very naive traffic simulation. This will only move the car to the end of the road then spawn it back at the start. Eventually this will be a more complex traffic simulation that should stress the ECS.从代码结构看Car组件携带offset、distance_traveled、dir道路通过Children查询其上的车辆该设计已为后续引入更复杂的交通仿真预留了组件与查询模式未来的仿真可以基于同样的Road/Car组件数据替换为包含加速、制动、避让、路口决策等逻辑的 ECS 系统。八、运行时设置面板Feathers UI 实时调参settings.rs 定义了Settings资源并实现了运行时 UIsettings_ui覆盖五个开关与一个动作控件对应设置生效方式Simulate Carssimulate_cars切换车辆移动系统是否运行Shadow maps enabledshadow_maps_enabled遍历所有DirectionalLight切换shadow_maps_enabledContact shadows enabledcontact_shadows_enabled遍历所有DirectionalLight切换contact_shadows_enabledWireframe Enabledwireframe_enabled修改WireframeConfig.global开启全场景线框CPU cullingcpu_culling遍历所有网格插入/移除NoCpuCulling组件Regenerate City—销毁CityRoot实体用随机新种子重建城市size32, car_density0.1面板实现还体现了 UI 与相机交互的细节鼠标悬停面板时通过OnPointerOver/OnPointerOut观察者禁用/启用FreeCameraState避免浏览面板时相机被误操作。UI 组件FeathersCheckbox、FeathersButton来自bevy_feathers组件库主题为深色主题。九、渲染管线大气、相机与后处理9.1 相机与后处理栈相机场景main.rs集成了HdrMsaa::OffHDR 渲染并关闭多重采样为 TAA 铺路TemporalAntiAliasing时间抗锯齿Bloom::NATURAL让太阳获得更自然的辉光ContactShadows接触阴影Exposure::OVERCAST场景方向光光照较强提高曝光补偿使画面处于舒适的亮度区间AtmosphereSettings { aerial_view_lut_max_distance: 1.6e4 }将天空透视查找表aerial view LUT的最大距离从默认 32km 缩小到 16km使其贴合城市尺度减少条带伪影。9.2 大气与近地薄雾spawn_atmospheremain.rs在地球大气介质上叠加了一层近地雾霾散射项ScatteringTerm雾霾标高scale height100 米形成低空浓雾层单次散射反照率0.99高反照、低吸收呈现白色能见度 12km通过 Koschmieder 关系换算消光系数beta_ext (3.912 / 12.0) * 1e-3相函数使用 Mie 散射不对称因子 0.76近似真实雾霾。大气实体整体缩放1/20并下移inner_radius * scale使得1 个城市街区 ≈ 100 米相对大气尺度从而让大气透视与雾效在城市的尺度下表现自然。该功能由AtmosphereEnvironmentMapLight组件接入让大气同时驱动反射与间接光照IBL。十、大型场景性能优化的四个抓手从源码可以提炼出本示例用于撑起大规模场景的四个优化手段它们也是阅读bevy_city最值得学习的内容静态变换优化StaticTransformOptimizationsmain.rs 中显式插入StaticTransformOptimizations::Enabledmain.rs。其底层原理见 crates/bevy_transform/src/systems.rs对于大量静态实体通过向上层传播脏位dirty bit变换传播阶段可以整体跳过未变更的子树如果场景非常动态追踪成本可能超过收益因此默认启用但可关闭。两级VisibilityRangeLOD建筑/树木/车辆每类物体都带高模与 LOD 替身配合交叉淡入淡出详见第六节在远处大幅减少三角形与 drawcall 压力。CPU 剔除开关NoCpuCullingNoCpuCulling组件定义于 crates/bevy_camera/src/visibility/mod.rs标记的网格跳过 CPU 剔除由 GPU 侧可见性系统接管。本示例既支持--no-cpu-culling启动参数统一添加也支持运行时 UI 逐个切换。网格合并与资产复用merge_car_meshes把每个车型的多网格场景合并为单网格道路通过拉伸/旋转单一路面资产复用树木、栅栏、路径等以WorldAssetRoot世界资产实例化多个实例共享同一份资产数据网格、材质显著降低内存与加载开销。十一、延伸阅读城市生成算法generate_city.rs资产加载与 LOD 解析assets.rs主流程与加载状态机main.rs运行时设置面板settings.rsVisibilityRange组件的完整语义与交叉淡入淡出契约crates/bevy_city/src/visibility/range.rs注文档路径为 crates/bevy_camera/src/visibility/range.rs世界资产实例化事件WorldInstanceReadycrates/bevy_world_serialization/src/world_asset_spawner.rs静态变换优化crates/bevy_transform/src/systems.rs同一examples/large_scenes目录下还有 bistro、caldera_hotel、zero_day、mipmap_generator 等其他大型场景示例可以与bevy_city互为参照了解不同形态下的大场景压力测试方式。赞分享游戏开发图形学【免费下载链接】bevyA refreshingly simple>项目地址https://gitcode.com/GitHub_Trending/be/bevy点击查看免费下载相关推荐Bevy Bistro 大型场景压测示例全指南场景资源、纹理压缩与性能基准测试Bevy Bistro 大型场景压测示例全指南场景资源、纹理压缩与性能基准测试 本篇技术指南以 Bevy 官方仓库中的 bistro 大场景示例为对象完整讲游戏开发图形学Unity ECS 实体图形学中的 LOD 系统实战HDRPSamples LODs 示例场景深度解析Unity ECS 实体图形学中的 LOD 系统实战HDRPSamples LODs 示例场景深度解析 本文以 EntityComponentSystemSa示例工程AutoLOD5步实现自动化LOD生成与场景性能优化AutoLOD5步实现自动化LOD生成与场景性能优化 AutoLOD是一款强大的自动化LODLevel of Detail生成与场景优化工具能够帮助开发图形学游戏开发开发工具上一篇化学AI智能体入门ChemCrow完整上手指南与工具链实战解析下一篇Path of Building终极指南从零开始掌握流放之路离线构建规划创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考