You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bevy中为GLTF场景的SceneBundle应用自定义材质

为Bevy中GLTF场景的实体替换自定义材质

问题描述

我使用Bevy框架开发3D游戏,通过asset_server.load()加载glTF文件并创建了SceneBundle,代码如下:

fn spawn_gltf(
    mut commands: Commands,
    ass: Res<AssetServer>,
) {
    let my_gltf = ass.load("my.glb#Scene0");
    commands.spawn(SceneBundle {
        scene: my_gltf,
        transform: Transform::from_xyz(2.0, 0.0, -5.0),
        ..Default::default()
    });
}

现在希望为该SceneBundle中的实体应用带有自定义顶点/片元着色器的CustomMaterial,但官方示例仅展示了如何为MaterialMeshBundle应用自定义材质,请问如何实现?

解决方案

加载GLTF场景时,Bevy会自动为场景内的网格实体生成带PbrMaterial的组件。要替换为自定义材质,需遍历场景下的相关实体,替换材质组件。

步骤1:标记场景根实体

为加载的SceneBundle添加自定义标签组件,方便后续识别其下的子实体:

// 定义标签组件
#[derive(Component)]
struct GltfSceneRoot;

fn spawn_gltf(
    mut commands: Commands,
    ass: Res<AssetServer>,
) {
    let my_gltf = ass.load("my.glb#Scene0");
    commands.spawn((
        SceneBundle {
            scene: my_gltf,
            transform: Transform::from_xyz(2.0, 0.0, -5.0),
            ..Default::default()
        },
        GltfSceneRoot, // 添加标签标记根实体
    ));
}

步骤2:实现材质替换系统

创建系统遍历场景根实体下的所有带PbrMaterial的网格实体,替换为CustomMaterial:

fn replace_gltf_materials(
    mut commands: Commands,
    root_query: Query<Entity, With<GltfSceneRoot>>,
    mesh_entity_query: Query<(Entity, &Handle<PbrMaterial>), Without<GltfSceneRoot>>,
    pbr_materials: Res<Assets<PbrMaterial>>,
    mut custom_materials: ResMut<Assets<CustomMaterial>>,
    hierarchy: Query<&Parent>,
) {
    // 获取场景根实体
    let Some(root_entity) = root_query.iter().next() else {
        return;
    };

    // 遍历所有带PbrMaterial的实体,检查是否属于目标场景
    for (entity, pbr_mat_handle) in mesh_entity_query.iter() {
        // 递归验证实体是否为场景根的子实体
        let mut current_entity = entity;
        let mut is_scene_child = false;
        while let Some(parent) = hierarchy.get(current_entity).ok() {
            if parent.get() == root_entity {
                is_scene_child = true;
                break;
            }
            current_entity = parent.get();
        }

        if !is_scene_child {
            continue;
        }

        // 可选:从原有PbrMaterial迁移属性到自定义材质
        let custom_mat = match pbr_materials.get(pbr_mat_handle) {
            Some(pbr_mat) => CustomMaterial {
                color: pbr_mat.base_color,
                color_texture: pbr_mat.base_color_texture.clone(),
                alpha_mode: pbr_mat.alpha_mode,
            },
            None => CustomMaterial {
                color: Color::WHITE,
                color_texture: None,
                alpha_mode: AlphaMode::Opaque,
            },
        };

        // 替换材质组件:移除PbrMaterial,添加CustomMaterial
        let custom_mat_handle = custom_materials.add(custom_mat);
        commands.entity(entity)
            .remove::<Handle<PbrMaterial>>()
            .insert(custom_mat_handle);
    }
}

步骤3:完善CustomMaterial实现

确保CustomMaterial正确实现Material trait,若需要自定义顶点着色器,需额外指定:

use bevy::{
    pbr::{Material, MaterialPlugin, AlphaMode},
    reflect::TypeUuid,
    render::render_resource::{AsBindGroup, ShaderRef},
    prelude::*,
};

#[derive(AsBindGroup, TypeUuid, Debug, Clone)]
#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"]
pub struct CustomMaterial {
    #[uniform(0)]
    color: Color,
    #[texture(1)]
    #[sampler(2)]
    color_texture: Option<Handle<Image>>,
    alpha_mode: AlphaMode,
}

impl Material for CustomMaterial {
    // 若使用自定义顶点着色器,替换为你的shader路径,否则用默认PBR顶点着色器
    fn vertex_shader() -> ShaderRef {
        // "shaders/custom_vertex.wgsl".into()
        ShaderRef::Default
    }

    fn fragment_shader() -> ShaderRef {
        "shaders/custom_material.wgsl".into()
    }

    fn alpha_mode(&self) -> AlphaMode {
        self.alpha_mode
    }
}

步骤4:注册系统和插件

将自定义材质插件和系统添加到App中,注意材质替换系统要在场景实例化后运行:

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugin(MaterialPlugin::<CustomMaterial>::default()) // 注册自定义材质插件
        .add_startup_system(spawn_gltf)
        // 放在PostUpdate阶段,确保场景已完成实例化
        .add_system(replace_gltf_materials.in_base_set(CoreSet::PostUpdate))
        .run();
}

注意事项

  • 场景加载是异步操作,替换材质的系统需在PostUpdate阶段执行,确保场景下的实体已全部生成
  • 该方法会替换场景中所有使用PbrMaterial的实体材质,若需针对性替换,可添加额外标签组件筛选
  • 自定义着色器的绑定组需与CustomMaterial的AsBindGroup属性完全匹配,否则会出现渲染错误

内容的提问来源于stack exchange,提问作者Youdi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 14:32:08