//! This example showcases light transmission //! //! ## Controls //! //! | Key Binding | Action | //! |:-------------------|:-----------------------------------------------------| //! | `J`/`K`/`L`/`;` | Change Screen Space Transmission Quality | //! | `O` / `P` | Decrease / Increase Screen Space Transmission Steps | //! | `1` / `2` | Decrease / Increase Diffuse Transmission | //! | `Q` / `W` | Decrease / Increase Specular Transmission | //! | `A` / `S` | Decrease / Increase Thickness | //! | `Z` / `X` | Decrease / Increase IOR | //! | `E` / `R` | Decrease / Increase Perceptual Roughness | //! | `U` / `I` | Decrease / Increase Reflectance | //! | Arrow Keys | Control Camera | //! | `C` | Randomize Colors | //! | `H` | Toggle HDR + Bloom | //! | `D` | Toggle Depth Prepass | //! | `T` | Toggle TAA | use std::f32::consts::PI; use bevy::{ color::palettes::css::*, core_pipeline::{ bloom::BloomSettings, core_3d::ScreenSpaceTransmissionQuality, prepass::DepthPrepass, tonemapping::Tonemapping, }, pbr::{NotShadowCaster, PointLightShadowMap, TransmittedShadowReceiver}, prelude::*, render::{ camera::{Exposure, TemporalJitter}, view::{ColorGrading, ColorGradingGlobal}, }, }; #[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))] use bevy::core_pipeline::experimental::taa::{ TemporalAntiAliasBundle, TemporalAntiAliasPlugin, TemporalAntiAliasSettings, }; use rand::random; fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins) .insert_resource(ClearColor(Color::BLACK)) .insert_resource(PointLightShadowMap { size: 2048 }) .insert_resource(AmbientLight { brightness: 0.0, ..default() }) .add_systems(Startup, setup) .add_systems(Update, (example_control_system, flicker_system)); // *Note:* TAA is not _required_ for specular transmission, but // it _greatly enhances_ the look of the resulting blur effects. // Sadly, it's not available under WebGL. #[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))] app.insert_resource(Msaa::Off) .add_plugins(TemporalAntiAliasPlugin); app.run(); } /// set up a simple 3D scene fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, asset_server: Res, ) { let icosphere_mesh = meshes.add(Sphere::new(0.9).mesh().ico(7).unwrap()); let cube_mesh = meshes.add(Cuboid::new(0.7, 0.7, 0.7)); let plane_mesh = meshes.add(Plane3d::default().mesh().size(2.0, 2.0)); let cylinder_mesh = meshes.add(Cylinder::new(0.5, 2.0).mesh().resolution(50)); // Cube #1 commands.spawn(( PbrBundle { mesh: cube_mesh.clone(), material: materials.add(StandardMaterial::default()), transform: Transform::from_xyz(0.25, 0.5, -2.0).with_rotation(Quat::from_euler( EulerRot::XYZ, 1.4, 3.7, 21.3, )), ..default() }, ExampleControls { color: true, specular_transmission: false, diffuse_transmission: false, }, )); // Cube #2 commands.spawn(( PbrBundle { mesh: cube_mesh, material: materials.add(StandardMaterial::default()), transform: Transform::from_xyz(-0.75, 0.7, -2.0).with_rotation(Quat::from_euler( EulerRot::XYZ, 0.4, 2.3, 4.7, )), ..default() }, ExampleControls { color: true, specular_transmission: false, diffuse_transmission: false, }, )); // Candle commands.spawn(( PbrBundle { mesh: cylinder_mesh, material: materials.add(StandardMaterial { base_color: Color::srgb(0.9, 0.2, 0.3), diffuse_transmission: 0.7, perceptual_roughness: 0.32, thickness: 0.2, ..default() }), transform: Transform::from_xyz(-1.0, 0.0, 0.0), ..default() }, ExampleControls { color: true, specular_transmission: false, diffuse_transmission: true, }, )); // Candle Flame let scaled_white = LinearRgba::from(ANTIQUE_WHITE) * 20.; let scaled_orange = LinearRgba::from(ORANGE_RED) * 4.; let emissive = LinearRgba { red: scaled_white.red + scaled_orange.red, green: scaled_white.green + scaled_orange.green, blue: scaled_white.blue + scaled_orange.blue, alpha: 1.0, }; commands.spawn(( PbrBundle { mesh: icosphere_mesh.clone(), material: materials.add(StandardMaterial { emissive, diffuse_transmission: 1.0, ..default() }), transform: Transform::from_xyz(-1.0, 1.15, 0.0).with_scale(Vec3::new(0.1, 0.2, 0.1)), ..default() }, Flicker, NotShadowCaster, )); // Glass Sphere commands.spawn(( PbrBundle { mesh: icosphere_mesh.clone(), material: materials.add(StandardMaterial { base_color: Color::WHITE, specular_transmission: 0.9, diffuse_transmission: 1.0, thickness: 1.8, ior: 1.5, perceptual_roughness: 0.12, ..default() }), transform: Transform::from_xyz(1.0, 0.0, 0.0), ..default() }, ExampleControls { color: true, specular_transmission: true, diffuse_transmission: false, }, )); // R Sphere commands.spawn(( PbrBundle { mesh: icosphere_mesh.clone(), material: materials.add(StandardMaterial { base_color: RED.into(), specular_transmission: 0.9, diffuse_transmission: 1.0, thickness: 1.8, ior: 1.5, perceptual_roughness: 0.12, ..default() }), transform: Transform::from_xyz(1.0, -0.5, 2.0).with_scale(Vec3::splat(0.5)), ..default() }, ExampleControls { color: true, specular_transmission: true, diffuse_transmission: false, }, )); // G Sphere commands.spawn(( PbrBundle { mesh: icosphere_mesh.clone(), material: materials.add(StandardMaterial { base_color: LIME.into(), specular_transmission: 0.9, diffuse_transmission: 1.0, thickness: 1.8, ior: 1.5, perceptual_roughness: 0.12, ..default() }), transform: Transform::from_xyz(0.0, -0.5, 2.0).with_scale(Vec3::splat(0.5)), ..default() }, ExampleControls { color: true, specular_transmission: true, diffuse_transmission: false, }, )); // B Sphere commands.spawn(( PbrBundle { mesh: icosphere_mesh, material: materials.add(StandardMaterial { base_color: BLUE.into(), specular_transmission: 0.9, diffuse_transmission: 1.0, thickness: 1.8, ior: 1.5, perceptual_roughness: 0.12, ..default() }), transform: Transform::from_xyz(-1.0, -0.5, 2.0).with_scale(Vec3::splat(0.5)), ..default() }, ExampleControls { color: true, specular_transmission: true, diffuse_transmission: false, }, )); // Chessboard Plane let black_material = materials.add(StandardMaterial { base_color: Color::BLACK, reflectance: 0.3, perceptual_roughness: 0.8, ..default() }); let white_material = materials.add(StandardMaterial { base_color: Color::WHITE, reflectance: 0.3, perceptual_roughness: 0.8, ..default() }); for x in -3..4 { for z in -3..4 { commands.spawn(( PbrBundle { mesh: plane_mesh.clone(), material: if (x + z) % 2 == 0 { black_material.clone() } else { white_material.clone() }, transform: Transform::from_xyz(x as f32 * 2.0, -1.0, z as f32 * 2.0), ..default() }, ExampleControls { color: true, specular_transmission: false, diffuse_transmission: false, }, )); } } // Paper commands.spawn(( PbrBundle { mesh: plane_mesh, material: materials.add(StandardMaterial { base_color: Color::WHITE, diffuse_transmission: 0.6, perceptual_roughness: 0.8, reflectance: 1.0, double_sided: true, cull_mode: None, ..default() }), transform: Transform::from_xyz(0.0, 0.5, -3.0) .with_scale(Vec3::new(2.0, 1.0, 1.0)) .with_rotation(Quat::from_euler(EulerRot::XYZ, PI / 2.0, 0.0, 0.0)), ..default() }, TransmittedShadowReceiver, ExampleControls { specular_transmission: false, color: false, diffuse_transmission: true, }, )); // Candle Light commands.spawn(( PointLightBundle { transform: Transform::from_xyz(-1.0, 1.7, 0.0), point_light: PointLight { color: Color::from( LinearRgba::from(ANTIQUE_WHITE).mix(&LinearRgba::from(ORANGE_RED), 0.2), ), intensity: 4_000.0, radius: 0.2, range: 5.0, shadows_enabled: true, ..default() }, ..default() }, Flicker, )); // Camera commands.spawn(( Camera3dBundle { camera: Camera { hdr: true, ..default() }, transform: Transform::from_xyz(1.0, 1.8, 7.0).looking_at(Vec3::ZERO, Vec3::Y), color_grading: ColorGrading { global: ColorGradingGlobal { post_saturation: 1.2, ..default() }, ..default() }, tonemapping: Tonemapping::TonyMcMapface, exposure: Exposure { ev100: 6.0 }, ..default() }, #[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))] TemporalAntiAliasBundle::default(), EnvironmentMapLight { intensity: 25.0, diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), }, BloomSettings::default(), )); // Controls Text let text_style = TextStyle::default(); commands.spawn(( TextBundle::from_section("", text_style).with_style(Style { position_type: PositionType::Absolute, top: Val::Px(12.0), left: Val::Px(12.0), ..default() }), ExampleDisplay, )); } #[derive(Component)] struct Flicker; #[derive(Component)] struct ExampleControls { diffuse_transmission: bool, specular_transmission: bool, color: bool, } struct ExampleState { diffuse_transmission: f32, specular_transmission: f32, thickness: f32, ior: f32, perceptual_roughness: f32, reflectance: f32, auto_camera: bool, } #[derive(Component)] struct ExampleDisplay; impl Default for ExampleState { fn default() -> Self { ExampleState { diffuse_transmission: 0.5, specular_transmission: 0.9, thickness: 1.8, ior: 1.5, perceptual_roughness: 0.12, reflectance: 0.5, auto_camera: true, } } } #[allow(clippy::too_many_arguments)] fn example_control_system( mut commands: Commands, mut materials: ResMut>, controllable: Query<(&Handle, &ExampleControls)>, mut camera: Query< ( Entity, &mut Camera, &mut Camera3d, &mut Transform, Option<&DepthPrepass>, Option<&TemporalJitter>, ), With, >, mut display: Query<&mut Text, With>, mut state: Local, time: Res