Path: blob/main/examples/3d/order_independent_transparency.rs
9367 views
//! A simple 3D scene showing how alpha blending can break and how order independent transparency (OIT) can fix it.1//!2//! See [`OrderIndependentTransparencyPlugin`] for the trade-offs of using OIT.3//!4//! [`OrderIndependentTransparencyPlugin`]: bevy::core_pipeline::oit::OrderIndependentTransparencyPlugin5use bevy::{6camera::visibility::RenderLayers,7color::palettes::css::{BLUE, GREEN, RED, YELLOW},8core_pipeline::{oit::OrderIndependentTransparencySettings, prepass::DepthPrepass},9prelude::*,10};1112fn main() {13App::new()14.add_plugins(DefaultPlugins)15.add_systems(Startup, setup)16.add_systems(Update, (toggle_oit, cycle_scenes))17.run();18}1920/// set up a simple 3D scene21fn setup(22mut commands: Commands,23mut meshes: ResMut<Assets<Mesh>>,24mut materials: ResMut<Assets<StandardMaterial>>,25) {26// camera27commands.spawn((28Camera3d::default(),29Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),30// Add this component to this camera to render transparent meshes using OIT31OrderIndependentTransparencySettings::default(),32RenderLayers::layer(1),33// Msaa currently doesn't work with OIT34Msaa::Off,35// Optional: depth prepass can help OIT filter out fragments occluded by opaque objects36DepthPrepass,37));3839// light40commands.spawn((41PointLight {42shadow_maps_enabled: false,43..default()44},45Transform::from_xyz(4.0, 8.0, 4.0),46RenderLayers::layer(1),47));4849// spawn help text50commands.spawn((51Text::default(),52Node {53position_type: PositionType::Absolute,54top: px(12),55left: px(12),56..default()57},58RenderLayers::layer(1),59children![60TextSpan::new("Press T to toggle OIT\n"),61TextSpan::new("OIT Enabled"),62TextSpan::new("\nPress C to cycle test scenes"),63],64));6566// spawn default scene67spawn_spheres(&mut commands, &mut meshes, &mut materials);68}6970fn toggle_oit(71mut commands: Commands,72text: Single<Entity, With<Text>>,73keyboard_input: Res<ButtonInput<KeyCode>>,74q: Single<(Entity, Has<OrderIndependentTransparencySettings>), With<Camera3d>>,75mut text_writer: TextUiWriter,76) {77if keyboard_input.just_pressed(KeyCode::KeyT) {78let (e, has_oit) = *q;79*text_writer.text(*text, 2) = if has_oit {80// Removing the component will completely disable OIT for this camera81commands82.entity(e)83.remove::<OrderIndependentTransparencySettings>();84"OIT disabled".to_string()85} else {86// Adding the component to the camera will render any transparent meshes87// with OIT instead of alpha blending88commands89.entity(e)90.insert(OrderIndependentTransparencySettings::default());91"OIT enabled".to_string()92};93}94}9596fn cycle_scenes(97mut commands: Commands,98keyboard_input: Res<ButtonInput<KeyCode>>,99mut meshes: ResMut<Assets<Mesh>>,100mut materials: ResMut<Assets<StandardMaterial>>,101q: Query<Entity, With<Mesh3d>>,102mut scene_id: Local<usize>,103asset_server: Res<AssetServer>,104) {105if keyboard_input.just_pressed(KeyCode::KeyC) {106// despawn current scene107for e in &q {108commands.entity(e).despawn();109}110// increment scene_id111*scene_id = (*scene_id + 1) % 4;112// spawn next scene113match *scene_id {1140 => spawn_spheres(&mut commands, &mut meshes, &mut materials),1151 => spawn_quads(&mut commands, &mut meshes, &mut materials),1162 => spawn_occlusion_test(&mut commands, &mut meshes, &mut materials),1173 => {118spawn_auto_instancing_test(119&mut commands,120&mut meshes,121&mut materials,122asset_server,123);124}125_ => unreachable!(),126}127}128}129130/// Spawns 3 overlapping spheres131/// Technically, when using `alpha_to_coverage` with MSAA this particular example wouldn't break,132/// but it breaks when disabling MSAA and is enough to show the difference between OIT enabled vs disabled.133fn spawn_spheres(134commands: &mut Commands,135meshes: &mut Assets<Mesh>,136materials: &mut Assets<StandardMaterial>,137) {138let pos_a = Vec3::new(-1.0, 0.75, 0.0);139let pos_b = Vec3::new(0.0, -0.75, 0.0);140let pos_c = Vec3::new(1.0, 0.75, 0.0);141142let offset = Vec3::new(0.0, 0.0, 0.0);143144let sphere_handle = meshes.add(Sphere::new(2.0).mesh());145146let alpha = 0.25;147148let render_layers = RenderLayers::layer(1);149150commands.spawn((151Mesh3d(sphere_handle.clone()),152MeshMaterial3d(materials.add(StandardMaterial {153base_color: RED.with_alpha(alpha).into(),154alpha_mode: AlphaMode::Blend,155..default()156})),157Transform::from_translation(pos_a + offset),158render_layers.clone(),159));160commands.spawn((161Mesh3d(sphere_handle.clone()),162MeshMaterial3d(materials.add(StandardMaterial {163base_color: GREEN.with_alpha(alpha).into(),164alpha_mode: AlphaMode::Blend,165..default()166})),167Transform::from_translation(pos_b + offset),168render_layers.clone(),169));170commands.spawn((171Mesh3d(sphere_handle.clone()),172MeshMaterial3d(materials.add(StandardMaterial {173base_color: BLUE.with_alpha(alpha).into(),174alpha_mode: AlphaMode::Blend,175..default()176})),177Transform::from_translation(pos_c + offset),178render_layers.clone(),179));180}181182fn spawn_quads(183commands: &mut Commands,184meshes: &mut Assets<Mesh>,185materials: &mut Assets<StandardMaterial>,186) {187let quad_handle = meshes.add(Rectangle::new(3.0, 3.0).mesh());188let render_layers = RenderLayers::layer(1);189let xform = |x, y, z| {190Transform::from_rotation(Quat::from_rotation_y(0.5))191.mul_transform(Transform::from_xyz(x, y, z))192};193commands.spawn((194Mesh3d(quad_handle.clone()),195MeshMaterial3d(materials.add(StandardMaterial {196base_color: RED.with_alpha(0.5).into(),197alpha_mode: AlphaMode::Blend,198..default()199})),200xform(1.0, -0.1, 0.),201render_layers.clone(),202));203commands.spawn((204Mesh3d(quad_handle.clone()),205MeshMaterial3d(materials.add(StandardMaterial {206base_color: BLUE.with_alpha(0.8).into(),207alpha_mode: AlphaMode::Blend,208..default()209})),210xform(0.5, 0.2, -0.5),211render_layers.clone(),212));213commands.spawn((214Mesh3d(quad_handle.clone()),215MeshMaterial3d(materials.add(StandardMaterial {216base_color: GREEN.with_green(1.0).with_alpha(0.5).into(),217alpha_mode: AlphaMode::Blend,218..default()219})),220xform(0.0, 0.4, -1.),221render_layers.clone(),222));223commands.spawn((224Mesh3d(quad_handle.clone()),225MeshMaterial3d(materials.add(StandardMaterial {226base_color: YELLOW.with_alpha(0.3).into(),227alpha_mode: AlphaMode::Blend,228..default()229})),230xform(-0.5, 0.6, -1.1),231render_layers.clone(),232));233commands.spawn((234Mesh3d(quad_handle.clone()),235MeshMaterial3d(materials.add(StandardMaterial {236base_color: BLUE.with_alpha(0.2).into(),237alpha_mode: AlphaMode::Blend,238..default()239})),240xform(-0.8, 0.8, -1.2),241render_layers.clone(),242));243}244245/// Spawn a combination of opaque cubes and transparent spheres.246/// This is useful to make sure transparent meshes drawn with OIT247/// are properly occluded by opaque meshes.248fn spawn_occlusion_test(249commands: &mut Commands,250meshes: &mut Assets<Mesh>,251materials: &mut Assets<StandardMaterial>,252) {253let sphere_handle = meshes.add(Sphere::new(1.0).mesh());254let cube_handle = meshes.add(Cuboid::from_size(Vec3::ONE).mesh());255let cube_material = materials.add(Color::srgb(0.8, 0.7, 0.6));256257let render_layers = RenderLayers::layer(1);258259// front260let x = -2.5;261commands.spawn((262Mesh3d(cube_handle.clone()),263MeshMaterial3d(cube_material.clone()),264Transform::from_xyz(x, 0.0, 2.0),265render_layers.clone(),266));267commands.spawn((268Mesh3d(sphere_handle.clone()),269MeshMaterial3d(materials.add(StandardMaterial {270base_color: RED.with_alpha(0.5).into(),271alpha_mode: AlphaMode::Blend,272..default()273})),274Transform::from_xyz(x, 0., 0.),275render_layers.clone(),276));277278// intersection279commands.spawn((280Mesh3d(cube_handle.clone()),281MeshMaterial3d(cube_material.clone()),282Transform::from_xyz(x, 0.0, 1.0),283render_layers.clone(),284));285commands.spawn((286Mesh3d(sphere_handle.clone()),287MeshMaterial3d(materials.add(StandardMaterial {288base_color: RED.with_alpha(0.5).into(),289alpha_mode: AlphaMode::Blend,290..default()291})),292Transform::from_xyz(0., 0., 0.),293render_layers.clone(),294));295296// back297let x = 2.5;298commands.spawn((299Mesh3d(cube_handle.clone()),300MeshMaterial3d(cube_material.clone()),301Transform::from_xyz(x, 0.0, -2.0),302render_layers.clone(),303));304commands.spawn((305Mesh3d(sphere_handle.clone()),306MeshMaterial3d(materials.add(StandardMaterial {307base_color: RED.with_alpha(0.5).into(),308alpha_mode: AlphaMode::Blend,309..default()310})),311Transform::from_xyz(x, 0., 0.),312render_layers.clone(),313));314}315316fn spawn_auto_instancing_test(317commands: &mut Commands,318meshes: &mut Assets<Mesh>,319materials: &mut Assets<StandardMaterial>,320asset_server: Res<AssetServer>,321) {322let render_layers = RenderLayers::layer(1);323324let cube = meshes.add(Cuboid::new(1.0, 1.0, 1.0));325let material_handle = materials.add(StandardMaterial {326alpha_mode: AlphaMode::Blend,327base_color_texture: Some(asset_server.load("textures/slice_square.png")),328..Default::default()329});330let mut bundles = Vec::with_capacity(3 * 3 * 3);331332for z in -1..=1 {333for y in -1..=1 {334for x in -1..=1 {335bundles.push((336Mesh3d(cube.clone()),337MeshMaterial3d(material_handle.clone()),338Transform::from_xyz(x as f32 * 2.0, y as f32 * 2.0, z as f32 * 2.0),339render_layers.clone(),340));341}342}343}344commands.spawn_batch(bundles);345}346347348