//! Demonstrates different sub view effects. //! //! A sub view is essentially a smaller section of a larger viewport. Some use //! cases include: //! - Split one image across multiple cameras, for use in a multimonitor setups //! - Magnify a section of the image, by rendering a small sub view in another //! camera //! - Rapidly change the sub view offset to get a screen shake effect use bevy::{ prelude::*, render::camera::{ScalingMode, SubCameraView, Viewport}, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, (move_camera_view, resize_viewports)) .run(); } #[derive(Debug, Component)] struct MovingCameraMarker; /// Set up a simple 3D scene fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { let transform = Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y); // Plane commands.spawn(( Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // Cube commands.spawn(( Mesh3d(meshes.add(Cuboid::default())), MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))), Transform::from_xyz(0.0, 0.5, 0.0), )); // Light commands.spawn(( PointLight { shadows_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), )); // Main perspective camera: // // The main perspective image to use as a comparison for the sub views. commands.spawn(( Camera3d::default(), Camera::default(), ExampleViewports::PerspectiveMain, transform, )); // Perspective camera right half: // // For this camera, the projection is perspective, and `size` is half the // width of the `full_size`, while the x value of `offset` is set to half // the value of the full width, causing the right half of the image to be // shown. Since the viewport has an aspect ratio of 1x1 and the sub view has // an aspect ratio of 1x2, the image appears stretched along the horizontal // axis. commands.spawn(( Camera3d::default(), Camera { sub_camera_view: Some(SubCameraView { // The values of `full_size` and `size` do not have to be the // exact values of your physical viewport. The important part is // the ratio between them. full_size: UVec2::new(10, 10), // The `offset` is also relative to the values in `full_size` // and `size` offset: Vec2::new(5.0, 0.0), size: UVec2::new(5, 10), }), order: 1, ..default() }, ExampleViewports::PerspectiveStretched, transform, )); // Perspective camera moving: // // For this camera, the projection is perspective, and the offset is updated // continuously in 150 units per second in `move_camera_view`. Since the // `full_size` is 500x500, the image should appear to be moving across the // full image once every 3.3 seconds. `size` is a fifth of the size of // `full_size`, so the image will appear zoomed in. commands.spawn(( Camera3d::default(), Camera { sub_camera_view: Some(SubCameraView { full_size: UVec2::new(500, 500), offset: Vec2::ZERO, size: UVec2::new(100, 100), }), order: 2, ..default() }, transform, ExampleViewports::PerspectiveMoving, MovingCameraMarker, )); // Perspective camera different aspect ratio: // // For this camera, the projection is perspective, and the aspect ratio of // the sub view (2x1) is different to the aspect ratio of the full view // (2x2). The aspect ratio of the sub view matches the aspect ratio of // the viewport and should show an unstretched image of the top half of the // full perspective image. commands.spawn(( Camera3d::default(), Camera { sub_camera_view: Some(SubCameraView { full_size: UVec2::new(800, 800), offset: Vec2::ZERO, size: UVec2::new(800, 400), }), order: 3, ..default() }, ExampleViewports::PerspectiveControl, transform, )); // Main orthographic camera: // // The main orthographic image to use as a comparison for the sub views. commands.spawn(( Camera3d::default(), Projection::from(OrthographicProjection { scaling_mode: ScalingMode::FixedVertical(6.0), ..OrthographicProjection::default_3d() }), Camera { order: 4, ..default() }, ExampleViewports::OrthographicMain, transform, )); // Orthographic camera left half: // // For this camera, the projection is orthographic, and `size` is half the // width of the `full_size`, causing the left half of the image to be shown. // Since the viewport has an aspect ratio of 1x1 and the sub view has an // aspect ratio of 1x2, the image appears stretched along the horizontal axis. commands.spawn(( Camera3d::default(), Projection::from(OrthographicProjection { scaling_mode: ScalingMode::FixedVertical(6.0), ..OrthographicProjection::default_3d() }), Camera { sub_camera_view: Some(SubCameraView { full_size: UVec2::new(2, 2), offset: Vec2::ZERO, size: UVec2::new(1, 2), }), order: 5, ..default() }, ExampleViewports::OrthographicStretched, transform, )); // Orthographic camera moving: // // For this camera, the projection is orthographic, and the offset is // updated continuously in 150 units per second in `move_camera_view`. Since // the `full_size` is 500x500, the image should appear to be moving across // the full image once every 3.3 seconds. `size` is a fifth of the size of // `full_size`, so the image will appear zoomed in. commands.spawn(( Camera3d::default(), Projection::from(OrthographicProjection { scaling_mode: ScalingMode::FixedVertical(6.0), ..OrthographicProjection::default_3d() }), Camera { sub_camera_view: Some(SubCameraView { full_size: UVec2::new(500, 500), offset: Vec2::ZERO, size: UVec2::new(100, 100), }), order: 6, ..default() }, transform, ExampleViewports::OrthographicMoving, MovingCameraMarker, )); // Orthographic camera different aspect ratio: // // For this camera, the projection is orthographic, and the aspect ratio of // the sub view (2x1) is different to the aspect ratio of the full view // (2x2). The aspect ratio of the sub view matches the aspect ratio of // the viewport and should show an unstretched image of the top half of the // full orthographic image. commands.spawn(( Camera3d::default(), Projection::from(OrthographicProjection { scaling_mode: ScalingMode::FixedVertical(6.0), ..OrthographicProjection::default_3d() }), Camera { sub_camera_view: Some(SubCameraView { full_size: UVec2::new(200, 200), offset: Vec2::ZERO, size: UVec2::new(200, 100), }), order: 7, ..default() }, ExampleViewports::OrthographicControl, transform, )); } fn move_camera_view( mut movable_camera_query: Query<&mut Camera, With>, time: Res