Skip to content

Make SubCameraView into a new primitive shape #19302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3952,13 +3952,13 @@ category = "3D Rendering"
wasm = false

[[example]]
name = "camera_sub_view"
path = "examples/3d/camera_sub_view.rs"
name = "camera_crop"
path = "examples/3d/camera_crop.rs"
doc-scrape-examples = true

[package.metadata.example.camera_sub_view]
[package.metadata.example.camera_crop]
name = "Camera sub view"
description = "Demonstrates using different sub view effects on a camera"
description = "Demonstrates using different camera crop effects"
category = "3D Rendering"
wasm = true

Expand Down
68 changes: 10 additions & 58 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{ClearColorConfig, Projection};
use crate::{
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
camera::{CameraProjection, ManualTextureViewHandle, ManualTextureViews},
primitives::Frustum,
primitives::{Frustum, SubRect},
render_asset::RenderAssets,
render_graph::{InternedRenderSubGraph, RenderSubGraph},
render_resource::TextureView,
Expand Down Expand Up @@ -112,55 +112,6 @@ impl Viewport {
}
}

/// Settings to define a camera sub view.
///
/// When [`Camera::sub_camera_view`] is `Some`, only the sub-section of the
/// image defined by `size` and `offset` (relative to the `full_size` of the
/// whole image) is projected to the cameras viewport.
///
/// Take the example of the following multi-monitor setup:
/// ```css
/// ┌───┬───┐
/// │ A │ B │
/// ├───┼───┤
/// │ C │ D │
/// └───┴───┘
/// ```
/// If each monitor is 1920x1080, the whole image will have a resolution of
/// 3840x2160. For each monitor we can use a single camera with a viewport of
/// the same size as the monitor it corresponds to. To ensure that the image is
/// cohesive, we can use a different sub view on each camera:
/// - Camera A: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 0,0
/// - Camera B: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 1920,0
/// - Camera C: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 0,1080
/// - Camera D: `full_size` = 3840x2160, `size` = 1920x1080, `offset` =
/// 1920,1080
///
/// However since only the ratio between the values is important, they could all
/// be divided by 120 and still produce the same image. Camera D would for
/// example have the following values:
/// `full_size` = 32x18, `size` = 16x9, `offset` = 16,9
#[derive(Debug, Clone, Copy, Reflect, PartialEq)]
#[reflect(Clone, PartialEq, Default)]
pub struct SubCameraView {
/// Size of the entire camera view
pub full_size: UVec2,
/// Offset of the sub camera
pub offset: Vec2,
/// Size of the sub camera
pub size: UVec2,
}

impl Default for SubCameraView {
fn default() -> Self {
Self {
full_size: UVec2::new(1, 1),
offset: Vec2::new(0., 0.),
size: UVec2::new(1, 1),
}
}
}

/// Information about the current [`RenderTarget`].
#[derive(Default, Debug, Clone)]
pub struct RenderTargetInfo {
Expand All @@ -180,7 +131,7 @@ pub struct ComputedCameraValues {
target_info: Option<RenderTargetInfo>,
// size of the `Viewport`
old_viewport_size: Option<UVec2>,
old_sub_camera_view: Option<SubCameraView>,
old_crop: Option<SubRect>,
}

/// How much energy a `Camera3d` absorbs from incoming light.
Expand Down Expand Up @@ -370,8 +321,9 @@ pub struct Camera {
pub msaa_writeback: bool,
/// The clear color operation to perform on the render target.
pub clear_color: ClearColorConfig,
/// If set, this camera will be a sub camera of a large view, defined by a [`SubCameraView`].
pub sub_camera_view: Option<SubCameraView>,
/// If set, this camera will still render to its entire viewport, but its projection will
/// adjust to only render the specified [`SubRect`] of the total view.
pub crop: Option<SubRect>,
}

fn warn_on_no_render_graph(world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
Expand All @@ -392,7 +344,7 @@ impl Default for Camera {
hdr: false,
msaa_writeback: true,
clear_color: Default::default(),
sub_camera_view: None,
crop: None,
}
}
}
Expand Down Expand Up @@ -992,7 +944,7 @@ pub fn camera_system(
|| camera.is_added()
|| camera_projection.is_changed()
|| camera.computed.old_viewport_size != viewport_size
|| camera.computed.old_sub_camera_view != camera.sub_camera_view
|| camera.computed.old_crop != camera.crop
{
let new_computed_target_info = normalized_target.get_render_target_info(
windows,
Expand Down Expand Up @@ -1038,7 +990,7 @@ pub fn camera_system(
if let Some(size) = camera.logical_viewport_size() {
if size.x != 0.0 && size.y != 0.0 {
camera_projection.update(size.x, size.y);
camera.computed.clip_from_view = match &camera.sub_camera_view {
camera.computed.clip_from_view = match &camera.crop {
Some(sub_view) => {
camera_projection.get_clip_from_view_for_sub(sub_view)
}
Expand All @@ -1053,8 +1005,8 @@ pub fn camera_system(
camera.computed.old_viewport_size = viewport_size;
}

if camera.computed.old_sub_camera_view != camera.sub_camera_view {
camera.computed.old_sub_camera_view = camera.sub_camera_view;
if camera.computed.old_crop != camera.crop {
camera.computed.old_crop = camera.crop;
}
}
}
Expand Down
37 changes: 20 additions & 17 deletions crates/bevy_render/src/camera/projection.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use core::fmt::Debug;

use crate::{primitives::Frustum, view::VisibilitySystems};
use crate::{
primitives::{Frustum, SubRect},
view::VisibilitySystems,
};
use bevy_app::{App, Plugin, PostStartup, PostUpdate};
use bevy_asset::AssetEventSystems;
use bevy_derive::{Deref, DerefMut};
Expand Down Expand Up @@ -70,7 +73,7 @@ pub trait CameraProjection {
fn get_clip_from_view(&self) -> Mat4;

/// Generate the projection matrix for a [`SubCameraView`](super::SubCameraView).
fn get_clip_from_view_for_sub(&self, sub_view: &super::SubCameraView) -> Mat4;
fn get_clip_from_view_for_sub(&self, sub_rect: &SubRect) -> Mat4;

/// When the area this camera renders to changes dimensions, this method will be automatically
/// called. Use this to update any projection properties that depend on the aspect ratio or
Expand Down Expand Up @@ -261,7 +264,7 @@ impl CameraProjection for Projection {
}
}

fn get_clip_from_view_for_sub(&self, sub_view: &super::SubCameraView) -> Mat4 {
fn get_clip_from_view_for_sub(&self, sub_view: &SubRect) -> Mat4 {
match self {
Projection::Perspective(projection) => projection.get_clip_from_view_for_sub(sub_view),
Projection::Orthographic(projection) => projection.get_clip_from_view_for_sub(sub_view),
Expand Down Expand Up @@ -337,14 +340,14 @@ impl CameraProjection for PerspectiveProjection {
Mat4::perspective_infinite_reverse_rh(self.fov, self.aspect_ratio, self.near)
}

fn get_clip_from_view_for_sub(&self, sub_view: &super::SubCameraView) -> Mat4 {
let full_width = sub_view.full_size.x as f32;
let full_height = sub_view.full_size.y as f32;
let sub_width = sub_view.size.x as f32;
let sub_height = sub_view.size.y as f32;
let offset_x = sub_view.offset.x;
fn get_clip_from_view_for_sub(&self, sub_rect: &SubRect) -> Mat4 {
let full_width = sub_rect.full_size.x as f32;
let full_height = sub_rect.full_size.y as f32;
let sub_width = sub_rect.size.x as f32;
let sub_height = sub_rect.size.y as f32;
let offset_x = sub_rect.offset.x;
// Y-axis increases from top to bottom
let offset_y = full_height - (sub_view.offset.y + sub_height);
let offset_y = full_height - (sub_rect.offset.y + sub_height);

let full_aspect = full_width / full_height;

Expand Down Expand Up @@ -565,13 +568,13 @@ impl CameraProjection for OrthographicProjection {
)
}

fn get_clip_from_view_for_sub(&self, sub_view: &super::SubCameraView) -> Mat4 {
let full_width = sub_view.full_size.x as f32;
let full_height = sub_view.full_size.y as f32;
let offset_x = sub_view.offset.x;
let offset_y = sub_view.offset.y;
let sub_width = sub_view.size.x as f32;
let sub_height = sub_view.size.y as f32;
fn get_clip_from_view_for_sub(&self, sub_rect: &SubRect) -> Mat4 {
let full_width = sub_rect.full_size.x as f32;
let full_height = sub_rect.full_size.y as f32;
let offset_x = sub_rect.offset.x;
let offset_y = sub_rect.offset.y;
let sub_width = sub_rect.size.x as f32;
let sub_height = sub_rect.size.y as f32;

let full_aspect = full_width / full_height;

Expand Down
Loading
Loading