Path: blob/main/examples/3d/order_independent_transparency.rs
6592 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},8core_pipeline::oit::OrderIndependentTransparencySettings,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));3637// light38commands.spawn((39PointLight {40shadows_enabled: false,41..default()42},43Transform::from_xyz(4.0, 8.0, 4.0),44RenderLayers::layer(1),45));4647// spawn help text48commands.spawn((49Text::default(),50Node {51position_type: PositionType::Absolute,52top: px(12),53left: px(12),54..default()55},56RenderLayers::layer(1),57children![58TextSpan::new("Press T to toggle OIT\n"),59TextSpan::new("OIT Enabled"),60TextSpan::new("\nPress C to cycle test scenes"),61],62));6364// spawn default scene65spawn_spheres(&mut commands, &mut meshes, &mut materials);66}6768fn toggle_oit(69mut commands: Commands,70text: Single<Entity, With<Text>>,71keyboard_input: Res<ButtonInput<KeyCode>>,72q: Single<(Entity, Has<OrderIndependentTransparencySettings>), With<Camera3d>>,73mut text_writer: TextUiWriter,74) {75if keyboard_input.just_pressed(KeyCode::KeyT) {76let (e, has_oit) = *q;77*text_writer.text(*text, 2) = if has_oit {78// Removing the component will completely disable OIT for this camera79commands80.entity(e)81.remove::<OrderIndependentTransparencySettings>();82"OIT disabled".to_string()83} else {84// Adding the component to the camera will render any transparent meshes85// with OIT instead of alpha blending86commands87.entity(e)88.insert(OrderIndependentTransparencySettings::default());89"OIT enabled".to_string()90};91}92}9394fn cycle_scenes(95mut commands: Commands,96keyboard_input: Res<ButtonInput<KeyCode>>,97mut meshes: ResMut<Assets<Mesh>>,98mut materials: ResMut<Assets<StandardMaterial>>,99q: Query<Entity, With<Mesh3d>>,100mut scene_id: Local<usize>,101) {102if keyboard_input.just_pressed(KeyCode::KeyC) {103// despawn current scene104for e in &q {105commands.entity(e).despawn();106}107// increment scene_id108*scene_id = (*scene_id + 1) % 2;109// spawn next scene110match *scene_id {1110 => spawn_spheres(&mut commands, &mut meshes, &mut materials),1121 => spawn_occlusion_test(&mut commands, &mut meshes, &mut materials),113_ => unreachable!(),114}115}116}117118/// Spawns 3 overlapping spheres119/// Technically, when using `alpha_to_coverage` with MSAA this particular example wouldn't break,120/// but it breaks when disabling MSAA and is enough to show the difference between OIT enabled vs disabled.121fn spawn_spheres(122commands: &mut Commands,123meshes: &mut Assets<Mesh>,124materials: &mut Assets<StandardMaterial>,125) {126let pos_a = Vec3::new(-1.0, 0.75, 0.0);127let pos_b = Vec3::new(0.0, -0.75, 0.0);128let pos_c = Vec3::new(1.0, 0.75, 0.0);129130let offset = Vec3::new(0.0, 0.0, 0.0);131132let sphere_handle = meshes.add(Sphere::new(2.0).mesh());133134let alpha = 0.25;135136let render_layers = RenderLayers::layer(1);137138commands.spawn((139Mesh3d(sphere_handle.clone()),140MeshMaterial3d(materials.add(StandardMaterial {141base_color: RED.with_alpha(alpha).into(),142alpha_mode: AlphaMode::Blend,143..default()144})),145Transform::from_translation(pos_a + offset),146render_layers.clone(),147));148commands.spawn((149Mesh3d(sphere_handle.clone()),150MeshMaterial3d(materials.add(StandardMaterial {151base_color: GREEN.with_alpha(alpha).into(),152alpha_mode: AlphaMode::Blend,153..default()154})),155Transform::from_translation(pos_b + offset),156render_layers.clone(),157));158commands.spawn((159Mesh3d(sphere_handle.clone()),160MeshMaterial3d(materials.add(StandardMaterial {161base_color: BLUE.with_alpha(alpha).into(),162alpha_mode: AlphaMode::Blend,163..default()164})),165Transform::from_translation(pos_c + offset),166render_layers.clone(),167));168}169170/// Spawn a combination of opaque cubes and transparent spheres.171/// This is useful to make sure transparent meshes drawn with OIT172/// are properly occluded by opaque meshes.173fn spawn_occlusion_test(174commands: &mut Commands,175meshes: &mut Assets<Mesh>,176materials: &mut Assets<StandardMaterial>,177) {178let sphere_handle = meshes.add(Sphere::new(1.0).mesh());179let cube_handle = meshes.add(Cuboid::from_size(Vec3::ONE).mesh());180let cube_material = materials.add(Color::srgb(0.8, 0.7, 0.6));181182let render_layers = RenderLayers::layer(1);183184// front185let x = -2.5;186commands.spawn((187Mesh3d(cube_handle.clone()),188MeshMaterial3d(cube_material.clone()),189Transform::from_xyz(x, 0.0, 2.0),190render_layers.clone(),191));192commands.spawn((193Mesh3d(sphere_handle.clone()),194MeshMaterial3d(materials.add(StandardMaterial {195base_color: RED.with_alpha(0.5).into(),196alpha_mode: AlphaMode::Blend,197..default()198})),199Transform::from_xyz(x, 0., 0.),200render_layers.clone(),201));202203// intersection204commands.spawn((205Mesh3d(cube_handle.clone()),206MeshMaterial3d(cube_material.clone()),207Transform::from_xyz(x, 0.0, 1.0),208render_layers.clone(),209));210commands.spawn((211Mesh3d(sphere_handle.clone()),212MeshMaterial3d(materials.add(StandardMaterial {213base_color: RED.with_alpha(0.5).into(),214alpha_mode: AlphaMode::Blend,215..default()216})),217Transform::from_xyz(0., 0., 0.),218render_layers.clone(),219));220221// back222let x = 2.5;223commands.spawn((224Mesh3d(cube_handle.clone()),225MeshMaterial3d(cube_material.clone()),226Transform::from_xyz(x, 0.0, -2.0),227render_layers.clone(),228));229commands.spawn((230Mesh3d(sphere_handle.clone()),231MeshMaterial3d(materials.add(StandardMaterial {232base_color: RED.with_alpha(0.5).into(),233alpha_mode: AlphaMode::Blend,234..default()235})),236Transform::from_xyz(x, 0., 0.),237render_layers.clone(),238));239}240241242