detached renderer from scene's Camera

- added `Observer`
- renamed `ObserverInfo` to `ObserverPosition`
- makes it easier to render scenes from arbitrary observers (like reflection probes)
This commit is contained in:
Dmitry Stepanov
2025-05-30 21:05:45 +03:00
parent 30049ffdd2
commit 93489c0e84
13 changed files with 257 additions and 190 deletions
+4 -4
View File
@@ -32,7 +32,7 @@ use crate::{
graph::{BaseSceneGraph, SceneGraph, SceneGraphNode},
gui::message::{KeyCode, KeyboardModifiers, MouseButton},
renderer::{
bundle::{ObserverInfo, RenderContext, RenderDataBundleStorage},
bundle::{ObserverPosition, RenderContext, RenderDataBundleStorage},
DynamicSurfaceCache,
},
scene::{
@@ -929,12 +929,12 @@ fn precise_ray_test(
ignore_back_faces: bool,
) -> PreciseRayTestResult {
let mut cache = DynamicSurfaceCache::new();
let observer_info = ObserverInfo::from_camera(camera);
let mut bundle_storage = RenderDataBundleStorage::new_empty(observer_info.clone());
let observer_position = ObserverPosition::from_camera(camera);
let mut bundle_storage = RenderDataBundleStorage::new_empty(observer_position.clone());
node.collect_render_data(&mut RenderContext {
render_mask: BitMask::all(),
elapsed_time: 0.0,
observer_info: &observer_info,
observer_position: &observer_position,
frustum: Some(&camera.frustum()),
storage: &mut bundle_storage,
graph,
+14 -19
View File
@@ -24,7 +24,7 @@ use crate::{
fxhash::FxHashSet,
graph::{BaseSceneGraph, SceneGraph},
renderer::{
bundle::{BundleRenderContext, ObserverInfo, RenderContext, RenderDataBundleStorage},
bundle::{BundleRenderContext, RenderContext, RenderDataBundleStorage},
cache::shader::{
binding, property, PropertyGroup, RenderMaterial, RenderPassContainer,
},
@@ -127,23 +127,14 @@ impl SceneRenderPass for HighlightRenderPass {
{
let render_pass_name = ImmutableString::new("Forward");
let observer_info = ObserverInfo {
observer_position: ctx.camera.global_position(),
z_near: ctx.camera.projection().z_near(),
z_far: ctx.camera.projection().z_far(),
view_matrix: ctx.camera.view_matrix(),
projection_matrix: ctx.camera.projection_matrix(),
};
let mut render_bundle_storage =
RenderDataBundleStorage::new_empty(observer_info.clone());
RenderDataBundleStorage::new_empty(ctx.observer.position.clone());
let frustum = ctx.camera.frustum();
let mut render_context = RenderContext {
render_mask: *ctx.camera.render_mask,
render_mask: ctx.observer.render_mask,
elapsed_time: ctx.elapsed_time,
observer_info: &observer_info,
frustum: Some(&frustum),
observer_position: &ctx.observer.position,
frustum: Some(&ctx.observer.frustum),
storage: &mut render_bundle_storage,
graph: &ctx.scene.graph,
render_pass_name: &render_pass_name,
@@ -160,8 +151,12 @@ impl SceneRenderPass for HighlightRenderPass {
render_bundle_storage.sort();
self.framebuffer
.clear(ctx.viewport, Some(Color::TRANSPARENT), Some(1.0), None);
self.framebuffer.clear(
ctx.observer.viewport,
Some(Color::TRANSPARENT),
Some(1.0),
None,
);
stats += render_bundle_storage.render_to_frame_buffer(
ctx.server,
@@ -178,7 +173,7 @@ impl SceneRenderPass for HighlightRenderPass {
fallback_resources: ctx.fallback_resources,
ambient_light: Default::default(),
scene_depth: Some(ctx.depth_texture),
viewport: ctx.viewport,
viewport: ctx.observer.viewport,
uniform_memory_allocator: ctx.uniform_memory_allocator,
},
)?;
@@ -186,7 +181,7 @@ impl SceneRenderPass for HighlightRenderPass {
// Render full screen quad with edge detect shader to draw outline of selected objects.
{
let frame_matrix = make_viewport_matrix(ctx.viewport);
let frame_matrix = make_viewport_matrix(ctx.observer.viewport);
let frame_texture = &self.framebuffer.color_attachments()[0].texture;
let color = Color::ORANGE.as_frgba();
@@ -207,7 +202,7 @@ impl SceneRenderPass for HighlightRenderPass {
&ImmutableString::new("Primary"),
ctx.framebuffer,
&self.quad,
ctx.viewport,
ctx.observer.viewport,
&material,
ctx.uniform_buffer_cache,
Default::default(),
+3 -3
View File
@@ -94,8 +94,8 @@ impl SceneRenderPass for OverlayRenderPass {
ctx: SceneRenderPassContext,
) -> Result<RenderPassStatistics, FrameworkError> {
let mut stats = RenderPassStatistics::default();
let view_projection = ctx.camera.view_projection_matrix();
let inv_view = ctx.camera.inv_view_matrix().unwrap();
let view_projection = ctx.observer.position.view_projection_matrix;
let inv_view = ctx.observer.position.view_matrix.try_inverse().unwrap();
let camera_up = -inv_view.up();
let camera_side = inv_view.side();
let sound_icon = ctx
@@ -135,7 +135,7 @@ impl SceneRenderPass for OverlayRenderPass {
&ImmutableString::new("Primary"),
ctx.framebuffer,
&self.quad,
ctx.viewport,
ctx.observer.viewport,
&material,
ctx.uniform_buffer_cache,
Default::default(),
+88 -37
View File
@@ -23,7 +23,7 @@
#![allow(missing_docs)] // TODO
use crate::material::Material;
use crate::scene::camera::Camera;
use crate::scene::camera::{Camera, ColorGradingLut, Exposure, Projection};
use crate::{
core::{
algebra::{Matrix4, Vector3, Vector4},
@@ -75,7 +75,7 @@ use crate::{
},
};
use fxhash::{FxBuildHasher, FxHashMap, FxHasher};
use fyrox_core::algebra::Point3;
use fyrox_core::algebra::{Point3, Vector2};
use fyrox_graph::{SceneGraph, SceneGraphNode};
use fyrox_graphics::gpu_program::{SamplerFallback, ShaderResourceDefinition};
use std::{
@@ -83,30 +83,76 @@ use std::{
hash::{Hash, Hasher},
};
/// Observer info contains all the data, that describes an observer. It could be a real camera, light source's
/// "virtual camera" that is used for shadow mapping, etc.
pub struct Observer {
pub handle: Handle<Node>,
pub render_target: Option<TextureResource>,
pub position: ObserverPosition,
pub environment_map: Option<TextureResource>,
pub skybox_map: Option<TextureResource>,
pub render_mask: BitMask,
pub projection: Projection,
pub color_grading_lut: Option<ColorGradingLut>,
pub color_grading_enabled: bool,
pub exposure: Exposure,
pub viewport: Rect<i32>,
pub frustum: Frustum,
}
impl Observer {
pub fn from_camera(camera: &Camera, mut frame_size: Vector2<f32>) -> Self {
let skybox_map = camera.skybox_ref().and_then(|s| s.cubemap.clone());
if let Some(render_target) = camera.render_target() {
if let Some(size) = render_target
.data_ref()
.as_loaded_ref()
.and_then(|rt| rt.kind().rectangle_size().map(|size| size.cast::<f32>()))
{
frame_size = size;
}
}
Observer {
handle: camera.handle(),
environment_map: camera.environment_map().or_else(|| skybox_map.clone()),
skybox_map,
render_mask: *camera.render_mask,
projection: camera.projection().clone(),
position: ObserverPosition::from_camera(camera),
render_target: camera.render_target().cloned(),
color_grading_lut: camera.color_grading_lut(),
color_grading_enabled: camera.color_grading_enabled(),
exposure: camera.exposure(),
viewport: camera.viewport_pixels(frame_size),
frustum: camera.frustum(),
}
}
}
/// Observer position contains all the data, that describes an observer position in 3D space. It
/// could be a real camera, light source's "virtual camera" that is used for shadow mapping, etc.
#[derive(Clone, Default)]
pub struct ObserverInfo {
pub struct ObserverPosition {
/// World-space position of the observer.
pub observer_position: Vector3<f32>,
/// Location of the near clipping plane.
pub translation: Vector3<f32>,
/// Position of the near clipping plane.
pub z_near: f32,
/// Location of the far clipping plane.
/// Position of the far clipping plane.
pub z_far: f32,
/// View matrix of the observer.
/// The view matrix of the observer.
pub view_matrix: Matrix4<f32>,
/// Projection matrix of the observer.
pub projection_matrix: Matrix4<f32>,
pub view_projection_matrix: Matrix4<f32>,
}
impl ObserverInfo {
impl ObserverPosition {
pub fn from_camera(camera: &Camera) -> Self {
ObserverInfo {
observer_position: camera.global_position(),
Self {
translation: camera.global_position(),
z_near: camera.projection().z_near(),
z_far: camera.projection().z_far(),
view_matrix: camera.view_matrix(),
projection_matrix: camera.projection_matrix(),
view_projection_matrix: camera.view_projection_matrix(),
}
}
}
@@ -121,7 +167,7 @@ pub struct RenderContext<'a> {
/// this value is **not** guaranteed to match real time. A user can change delta time with
/// which the engine "ticks" and this delta time affects elapsed time.
pub elapsed_time: f32,
pub observer_info: &'a ObserverInfo,
pub observer_position: &'a ObserverPosition,
/// Frustum of the observer, it is built using observer's view and projection matrix. Use the frustum to do
/// frustum culling.
pub frustum: Option<&'a Frustum>,
@@ -144,7 +190,7 @@ impl RenderContext<'_> {
const RANGE_CENTER: u64 = u64::MAX / 2;
const GRANULARITY: f32 = 1000.0;
let view_matrix = &self.observer_info.view_matrix;
let view_matrix = &self.observer_position.view_matrix;
let world_space_point = Point3::from(global_position);
let view_space_point = view_matrix.transform_point(&world_space_point);
@@ -823,7 +869,8 @@ pub struct LightSource {
/// rendering by reducing amount of state changes of OpenGL context.
pub struct RenderDataBundleStorage {
bundle_map: FxHashMap<u64, usize>,
pub observer_info: ObserverInfo,
/// Position of an observer for which this data bundle was created.
pub observer_position: ObserverPosition,
/// A sorted list of bundles.
pub bundles: Vec<RenderDataBundle>,
pub light_sources: Vec<LightSource>,
@@ -842,10 +889,10 @@ impl Default for RenderDataBundleStorageOptions {
}
impl RenderDataBundleStorage {
pub fn new_empty(observer_info: ObserverInfo) -> Self {
pub fn new_empty(observer_position: ObserverPosition) -> Self {
Self {
bundle_map: Default::default(),
observer_info,
observer_position,
bundles: Default::default(),
light_sources: Default::default(),
}
@@ -858,7 +905,7 @@ impl RenderDataBundleStorage {
graph: &Graph,
render_mask: BitMask,
elapsed_time: f32,
observer_info: ObserverInfo,
observer_position: &ObserverPosition,
render_pass_name: ImmutableString,
options: RenderDataBundleStorageOptions,
dynamic_surface_cache: &mut DynamicSurfaceCache,
@@ -867,13 +914,13 @@ impl RenderDataBundleStorage {
let capacity = graph.node_count() as usize;
let mut storage = Self {
bundle_map: FxHashMap::with_capacity_and_hasher(capacity, FxBuildHasher::default()),
observer_info: observer_info.clone(),
observer_position: observer_position.clone(),
bundles: Vec::with_capacity(capacity),
light_sources: Default::default(),
};
let frustum = Frustum::from_view_projection_matrix(
observer_info.projection_matrix * observer_info.view_matrix,
observer_position.projection_matrix * observer_position.view_matrix,
)
.unwrap_or_default();
@@ -883,11 +930,12 @@ impl RenderDataBundleStorage {
for level in lod_group.levels.iter() {
for &object in level.objects.iter() {
if let Some(object_ref) = graph.try_get(object) {
let distance = observer_info
.observer_position
let distance = observer_position
.translation
.metric_distance(&object_ref.global_position());
let z_range = observer_info.z_far - observer_info.z_near;
let normalized_distance = (distance - observer_info.z_near) / z_range;
let z_range = observer_position.z_far - observer_position.z_near;
let normalized_distance =
(distance - observer_position.z_near) / z_range;
let visible = normalized_distance >= level.begin()
&& normalized_distance <= level.end();
lod_filter[object.index() as usize] = visible;
@@ -948,7 +996,7 @@ impl RenderDataBundleStorage {
let mut ctx = RenderContext {
render_mask,
elapsed_time,
observer_info: &observer_info,
observer_position,
frustum: Some(&frustum),
storage: &mut storage,
graph,
@@ -1042,21 +1090,22 @@ impl RenderDataBundleStorage {
// Upload camera uniforms.
let inv_view = self
.observer_info
.observer_position
.view_matrix
.try_inverse()
.unwrap_or_default();
let view_projection = self.observer_info.projection_matrix * self.observer_info.view_matrix;
let view_projection =
self.observer_position.projection_matrix * self.observer_position.view_matrix;
let camera_up = inv_view.up();
let camera_side = inv_view.side();
let camera_uniforms = StaticUniformBuffer::<512>::new()
.with(&view_projection)
.with(&self.observer_info.observer_position)
.with(&self.observer_position.translation)
.with(&camera_up)
.with(&camera_side)
.with(&self.observer_info.z_near)
.with(&self.observer_info.z_far)
.with(&(self.observer_info.z_far - self.observer_info.z_near));
.with(&self.observer_position.z_near)
.with(&self.observer_position.z_far)
.with(&(self.observer_position.z_far - self.observer_position.z_near));
let camera_block = render_context
.uniform_memory_allocator
.allocate(camera_uniforms);
@@ -1089,7 +1138,8 @@ impl RenderDataBundleStorage {
{
let global_uniforms = self.write_global_uniform_blocks(&mut render_context);
let view_projection = self.observer_info.projection_matrix * self.observer_info.view_matrix;
let view_projection =
self.observer_position.projection_matrix * self.observer_position.view_matrix;
let mut bundle_uniform_data_set = Vec::with_capacity(self.bundles.len());
for bundle in self.bundles.iter() {
if !bundle_filter(bundle) {
@@ -1228,26 +1278,27 @@ impl RenderDataBundleStorageTrait for RenderDataBundleStorage {
#[cfg(test)]
mod test {
use crate::renderer::bundle::{ObserverInfo, RenderContext, RenderDataBundleStorage};
use crate::renderer::bundle::{ObserverPosition, RenderContext, RenderDataBundleStorage};
use fyrox_core::algebra::{Matrix4, Vector3};
//noinspection ALL
#[test]
fn test_calculate_sorting_index() {
let observer_info = ObserverInfo {
observer_position: Default::default(),
let observer_position = ObserverPosition {
translation: Default::default(),
z_near: 0.0,
z_far: 0.0,
view_matrix: Matrix4::identity(),
projection_matrix: Matrix4::identity(),
view_projection_matrix: Matrix4::identity(),
};
let render_context = RenderContext {
render_mask: Default::default(),
elapsed_time: 0.0,
observer_info: &observer_info.clone(),
observer_position: &observer_position.clone(),
frustum: None,
storage: &mut RenderDataBundleStorage::new_empty(observer_info),
storage: &mut RenderDataBundleStorage::new_empty(observer_position),
graph: &Default::default(),
render_pass_name: &Default::default(),
dynamic_surface_cache: &mut Default::default(),
+13 -10
View File
@@ -29,6 +29,7 @@
//! Every alpha channel is used for layer blending for terrains. This is inefficient, but for
//! now I don't know better solution.
use crate::renderer::bundle::Observer;
use crate::{
core::{
algebra::{Matrix4, Vector2},
@@ -58,7 +59,6 @@ use crate::{
FallbackResources, GeometryCache, QualitySettings, RenderPassStatistics, TextureCache,
},
scene::{
camera::Camera,
decal::Decal,
graph::Graph,
mesh::{surface::SurfaceData, RenderPath},
@@ -79,7 +79,7 @@ pub struct GBuffer {
pub(crate) struct GBufferRenderContext<'a, 'b> {
pub server: &'a dyn GraphicsServer,
pub camera: &'b Camera,
pub observer: &'b Observer,
pub geom_cache: &'a mut GeometryCache,
pub bundle_storage: &'a RenderDataBundleStorage,
pub texture_cache: &'a mut TextureCache,
@@ -200,7 +200,7 @@ impl GBuffer {
let GBufferRenderContext {
server,
camera,
observer,
geom_cache,
bundle_storage,
texture_cache,
@@ -214,8 +214,6 @@ impl GBuffer {
..
} = args;
let view_projection = camera.view_projection_matrix();
if quality_settings.use_occlusion_culling {
self.occlusion_tester.try_query_visibility_results(graph);
};
@@ -231,7 +229,7 @@ impl GBuffer {
let grid_cell = self
.occlusion_tester
.grid_cache
.cell(camera.global_position());
.cell(observer.position.translation);
let instance_filter = |instance: &SurfaceInstanceData| {
!quality_settings.use_occlusion_culling
@@ -272,14 +270,18 @@ impl GBuffer {
unit_quad,
objects.iter(),
&self.framebuffer,
camera.global_position(),
view_projection,
observer.position.translation,
observer.position.view_projection_matrix,
uniform_buffer_cache,
fallback_resources,
)?;
}
let inv_view_proj = view_projection.try_inverse().unwrap_or_default();
let inv_view_proj = observer
.position
.view_projection_matrix
.try_inverse()
.unwrap_or_default();
let depth = self.depth();
let decal_mask = self.decal_mask_texture();
let resolution = Vector2::new(self.width as f32, self.height as f32);
@@ -289,7 +291,8 @@ impl GBuffer {
// decals do not modify depth (only diffuse and normal maps).
let unit_cube = &self.cube;
for decal in graph.linear_iter().filter_map(|n| n.cast::<Decal>()) {
let world_view_proj = view_projection * decal.global_transform();
let world_view_proj =
observer.position.view_projection_matrix * decal.global_transform();
let diffuse_texture = decal
.diffuse_texture()
+52 -40
View File
@@ -18,6 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use crate::renderer::bundle::Observer;
use crate::renderer::DynamicSurfaceCache;
use crate::{
core::{
@@ -54,7 +55,6 @@ use crate::{
RenderPassStatistics, TextureCache,
},
scene::{
camera::Camera,
mesh::{
buffer::{TriangleBuffer, VertexBuffer},
surface::SurfaceData,
@@ -87,7 +87,7 @@ pub(crate) struct DeferredRendererContext<'a> {
pub elapsed_time: f32,
pub server: &'a dyn GraphicsServer,
pub scene: &'a Scene,
pub camera: &'a Camera,
pub observer: &'a Observer,
pub gbuffer: &'a mut GBuffer,
pub ambient_color: Color,
pub render_data_bundle: &'a RenderDataBundleStorage,
@@ -298,7 +298,7 @@ impl DeferredLightRenderer {
elapsed_time,
server,
scene,
camera,
observer,
gbuffer,
render_data_bundle,
shader_cache,
@@ -315,38 +315,41 @@ impl DeferredLightRenderer {
} = args;
let viewport = Rect::new(0, 0, gbuffer.width, gbuffer.height);
let frustum = Frustum::from_view_projection_matrix(camera.view_projection_matrix())
.unwrap_or_default();
let frustum =
Frustum::from_view_projection_matrix(observer.position.view_projection_matrix)
.unwrap_or_default();
let frame_matrix = make_viewport_matrix(viewport);
let projection_matrix = camera.projection_matrix();
let view_projection = camera.view_projection_matrix();
let inv_projection = projection_matrix.try_inverse().unwrap_or_default();
let inv_view_projection = view_projection.try_inverse().unwrap_or_default();
let camera_global_position = camera.global_position();
let inv_projection = observer
.position
.projection_matrix
.try_inverse()
.unwrap_or_default();
let inv_view_projection = observer
.position
.view_projection_matrix
.try_inverse()
.unwrap_or_default();
// Fill SSAO map.
if settings.use_ssao {
pass_stats += self.ssao_renderer.render(
gbuffer,
projection_matrix,
camera.view_matrix().basis(),
observer.position.projection_matrix,
observer.position.view_matrix.basis(),
uniform_buffer_cache,
fallback_resources,
)?;
}
// Render skybox (if any).
if let Some(skybox) = camera.skybox_ref() {
if let Some(texture_sampler_pair) = skybox
.cubemap_ref()
.and_then(|cube_map| textures.get(server, cube_map))
{
let size = camera.projection().z_far() / 2.0f32.sqrt();
if let Some(skybox) = observer.skybox_map.as_ref() {
if let Some(texture_sampler_pair) = textures.get(server, skybox) {
let size = observer.position.z_far / 2.0f32.sqrt();
let scale = Matrix4::new_scaling(size);
let wvp = Matrix4::new_translation(&camera.global_position()) * scale;
let wvp = view_projection * wvp;
let wvp = Matrix4::new_translation(&observer.position.translation) * scale;
let wvp = observer.position.view_projection_matrix * wvp;
let properties = PropertyGroup::from([property("worldViewProjection", &wvp)]);
let material = RenderMaterial::from([
binding(
@@ -376,9 +379,9 @@ impl DeferredLightRenderer {
}
}
let environment_map = camera
.skybox_ref()
.and_then(|s| s.cubemap_ref())
let environment_map = observer
.environment_map
.as_ref()
.and_then(|c| {
textures
.get(server, c)
@@ -401,7 +404,7 @@ impl DeferredLightRenderer {
let properties = PropertyGroup::from([
property("worldViewProjection", &frame_matrix),
property("ambientColor", &ambient_color),
property("cameraPosition", &camera_global_position),
property("cameraPosition", &observer.position.translation),
property("invViewProj", &inv_view_projection),
]);
let material = RenderMaterial::from([
@@ -462,7 +465,7 @@ impl DeferredLightRenderer {
)?;
for light in render_data_bundle.light_sources.iter() {
let distance_to_camera = (light.position - camera.global_position()).norm();
let distance_to_camera = (light.position - observer.position.translation).norm();
let (
raw_radius,
@@ -547,7 +550,11 @@ impl DeferredLightRenderer {
let b1 = shadows_distance * 0.2;
let b2 = shadows_distance * 0.4;
let cascade_index = if distance_to_camera < b1
|| (camera.global_position().metric_distance(&light.position) <= light_radius)
|| (observer
.position
.translation
.metric_distance(&light.position)
<= light_radius)
{
0
} else if distance_to_camera > b1 && distance_to_camera < b2 {
@@ -566,7 +573,7 @@ impl DeferredLightRenderer {
let mut light_view_projection = Matrix4::identity();
// Mark lit areas in stencil buffer to do light calculations only on them.
let shape_wvp_matrix = view_projection * bounding_shape_matrix;
let shape_wvp_matrix = observer.position.view_projection_matrix * bounding_shape_matrix;
for (cull_face, stencil_action) in [
(CullFace::Front, StencilAction::Incr),
(CullFace::Back, StencilAction::Decr),
@@ -611,10 +618,16 @@ impl DeferredLightRenderer {
if !matches!(light.kind, LightSourceKind::Directional { .. })
&& settings.use_light_occlusion_culling
{
if visibility_cache.needs_occlusion_query(camera_global_position, light.handle) {
if visibility_cache
.needs_occlusion_query(observer.position.translation, light.handle)
{
// Draw full screen quad, that will be used to count pixels that passed the stencil test
// on the stencil buffer's content generated by two previous drawing commands.
visibility_cache.begin_query(server, camera_global_position, light.handle)?;
visibility_cache.begin_query(
server,
observer.position.translation,
light.handle,
)?;
let properties =
PropertyGroup::from([property("worldViewProjection", &frame_matrix)]);
let material = RenderMaterial::from([binding("properties", &properties)]);
@@ -632,7 +645,7 @@ impl DeferredLightRenderer {
visibility_cache.end_query();
}
if !visibility_cache.is_visible(camera_global_position, light.handle) {
if !visibility_cache.is_visible(observer.position.translation, light.handle) {
needs_lighting = false;
}
}
@@ -665,7 +678,7 @@ impl DeferredLightRenderer {
pass_stats += self.spot_shadow_map_renderer.render(
server,
&scene.graph,
*camera.render_mask,
observer.render_mask,
elapsed_time,
light.position,
light_view_matrix,
@@ -687,7 +700,7 @@ impl DeferredLightRenderer {
pass_stats +=
self.point_shadow_map_renderer
.render(PointShadowMapRenderContext {
render_mask: *camera.render_mask,
render_mask: observer.render_mask,
elapsed_time,
state: server,
graph: &scene.graph,
@@ -711,7 +724,7 @@ impl DeferredLightRenderer {
state: server,
graph: &scene.graph,
light,
camera,
observer,
geom_cache: geometry_cache,
shader_cache,
texture_cache: textures,
@@ -774,7 +787,7 @@ impl DeferredLightRenderer {
property("invViewProj", &inv_view_projection),
property("lightPos", &light.position),
property("lightColor", &color),
property("cameraPosition", &camera_global_position),
property("cameraPosition", &observer.position.translation),
property("lightDirection", &emit_direction),
property("lightRadius", &light_radius),
property("halfHotspotConeAngleCos", &half_hotspot_cone_angle_cos),
@@ -844,7 +857,7 @@ impl DeferredLightRenderer {
property("invViewProj", &inv_view_projection),
property("lightPos", &light.position),
property("lightColor", &color),
property("cameraPosition", &camera_global_position),
property("cameraPosition", &observer.position.translation),
property("lightRadius", &light_radius),
property("shadowBias", &shadow_bias),
property("lightIntensity", &light.intensity),
@@ -916,15 +929,14 @@ impl DeferredLightRenderer {
];
let shadow_map_inv_size = 1.0 / (self.csm_renderer.size() as f32);
let shadow_bias = csm_options.shadow_bias();
let view_matrix = camera.view_matrix();
let properties = PropertyGroup::from([
property("worldViewProjection", &frame_matrix),
property("viewMatrix", &view_matrix),
property("viewMatrix", &observer.position.view_matrix),
property("invViewProj", &inv_view_projection),
property("lightViewProjMatrices", matrices.as_slice()),
property("lightColor", &color),
property("lightDirection", &emit_direction),
property("cameraPosition", &camera_global_position),
property("cameraPosition", &observer.position.translation),
property("lightIntensity", &light.intensity),
property("shadowsEnabled", &shadows_enabled),
property("shadowBias", &shadow_bias),
@@ -1006,9 +1018,9 @@ impl DeferredLightRenderer {
light,
gbuffer,
&self.quad,
camera.view_matrix(),
observer.position.view_matrix,
inv_projection,
view_projection,
observer.position.view_projection_matrix,
viewport,
&scene.graph,
frame_buffer,
+50 -48
View File
@@ -49,6 +49,7 @@ mod shadow;
mod ssao;
mod stats;
use crate::renderer::bundle::Observer;
use crate::scene::node::Node;
use crate::{
asset::{event::ResourceEvent, manager::ResourceManager},
@@ -65,12 +66,11 @@ use crate::{
uuid_provider,
},
engine::error::EngineError,
graph::SceneGraph,
gui::draw::DrawingContext,
material::shader::{Shader, ShaderDefinition},
renderer::{
bloom::BloomRenderer,
bundle::{ObserverInfo, RenderDataBundleStorage, RenderDataBundleStorageOptions},
bundle::{RenderDataBundleStorage, RenderDataBundleStorageOptions},
cache::{
geometry::GeometryCache,
shader::{
@@ -899,10 +899,7 @@ pub struct SceneRenderPassContext<'a, 'b> {
pub scene: &'b Scene,
/// A camera from the scene that is used as "eyes".
pub camera: &'b Camera,
/// A viewport of the camera.
pub viewport: Rect<i32>,
pub observer: &'b Observer,
/// A handle of the scene being rendered.
pub scene_handle: Handle<Scene>,
@@ -1038,6 +1035,24 @@ fn render_target_size(render_target: &TextureResource) -> Result<Vector2<f32>, F
})
}
fn collect_observers(scene: &Scene, frame_size: Vector2<f32>) -> Vec<Observer> {
scene
.graph
.linear_iter()
.filter_map(|node| {
if node.is_globally_enabled() {
if let Some(camera) = node.cast::<Camera>() {
if camera.is_enabled() {
return Some(camera);
}
}
}
None
})
.map(|camera| Observer::from_camera(camera, frame_size))
.collect()
}
impl Renderer {
/// Creates a new renderer with the given graphics server.
pub fn new(
@@ -1520,19 +1535,13 @@ impl Renderer {
.camera_data
.retain(|h, _| graph.is_valid_handle(*h));
for (camera_handle, camera) in graph.pair_iter().filter_map(|(handle, node)| {
if node.is_globally_enabled() {
if let Some(camera) = node.cast::<Camera>() {
if camera.is_enabled() {
return Some((handle, camera));
}
}
}
None
}) {
let (render_data, rt_size) = if let Some(render_target) = camera.render_target() {
let observers = collect_observers(scene, frame_size);
for observer in &observers {
let render_data = if let Some(render_target) = observer.render_target.as_ref() {
let rt_size = render_target_size(render_target)?;
let camera_render_data = match scene_render_data.camera_data.entry(camera_handle) {
let camera_render_data = match scene_render_data.camera_data.entry(observer.handle)
{
Entry::Occupied(entry) => {
let camera_render_data = entry.into_mut();
recreate_render_data_if_needed(
@@ -1546,7 +1555,8 @@ impl Renderer {
Entry::Vacant(entry) => {
let render_data = entry.insert(RenderDataContainer::new(server, rt_size)?);
info!(
"A new associated scene rendering data was created for camera {camera_handle}!"
"A new associated scene rendering data was created for camera {}!",
observer.handle
);
render_data
}
@@ -1558,26 +1568,20 @@ impl Renderer {
camera_render_data.ldr_scene_frame_texture().clone(),
)?;
(camera_render_data, rt_size)
camera_render_data
} else {
(&mut scene_render_data.scene_data, frame_size)
&mut scene_render_data.scene_data
};
let visibility_cache = self.visibility_cache.get_or_register(graph, camera_handle);
let viewport = camera.viewport_pixels(rt_size);
let visibility_cache = self
.visibility_cache
.get_or_register(graph, observer.handle);
let bundle_storage = RenderDataBundleStorage::from_graph(
graph,
*camera.render_mask,
observer.render_mask,
elapsed_time,
ObserverInfo {
observer_position: camera.global_position(),
z_near: camera.projection().z_near(),
z_far: camera.projection().z_far(),
view_matrix: camera.view_matrix(),
projection_matrix: camera.projection_matrix(),
},
&observer.position,
GBUFFER_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
collect_lights: true,
@@ -1592,7 +1596,7 @@ impl Renderer {
render_data.statistics += render_data.gbuffer.fill(GBufferRenderContext {
server,
camera,
observer,
geom_cache: &mut self.geometry_cache,
bundle_storage: &bundle_storage,
texture_cache: &mut self.texture_cache,
@@ -1611,7 +1615,7 @@ impl Renderer {
render_data.copy_depth_stencil_to_scene_framebuffer();
render_data.hdr_scene_framebuffer.clear(
viewport,
observer.viewport,
Some(
scene
.rendering_options
@@ -1628,7 +1632,7 @@ impl Renderer {
elapsed_time,
server,
scene,
camera,
observer,
gbuffer: &mut render_data.gbuffer,
ambient_color: scene.rendering_options.ambient_lighting_color,
render_data_bundle: &bundle_storage,
@@ -1656,7 +1660,7 @@ impl Renderer {
shader_cache: &mut self.shader_cache,
bundle_storage: &bundle_storage,
framebuffer: &render_data.hdr_scene_framebuffer,
viewport,
viewport: observer.viewport,
quality_settings: &self.quality_settings,
fallback_resources: &self.fallback_resources,
scene_depth: depth,
@@ -1676,9 +1680,8 @@ impl Renderer {
shader_cache: &mut self.shader_cache,
quality_settings: &self.quality_settings,
bundle_storage: &bundle_storage,
viewport,
scene,
camera,
observer,
scene_handle,
fallback_resources: &self.fallback_resources,
depth_texture: render_data.gbuffer.depth(),
@@ -1708,12 +1711,12 @@ impl Renderer {
render_data.hdr_scene_frame_texture(),
render_data.bloom_renderer.result(),
&render_data.ldr_scene_framebuffer,
viewport,
observer.viewport,
quad,
dt,
camera.exposure(),
camera.color_grading_lut_ref(),
camera.color_grading_enabled(),
observer.exposure,
observer.color_grading_lut.as_ref(),
observer.color_grading_enabled,
&mut self.texture_cache,
&mut self.uniform_buffer_cache,
&self.fallback_resources,
@@ -1722,7 +1725,7 @@ impl Renderer {
// Apply FXAA if needed.
if self.quality_settings.fxaa {
render_data.statistics += self.fxaa_renderer.render(
viewport,
observer.viewport,
render_data.ldr_scene_frame_texture(),
&render_data.ldr_temp_framebuffer,
&mut self.uniform_buffer_cache,
@@ -1736,7 +1739,7 @@ impl Renderer {
&render_data.ldr_scene_framebuffer,
temp_frame_texture,
&self.blit_shader,
viewport,
observer.viewport,
quad,
&self.fallback_resources,
)?;
@@ -1746,9 +1749,9 @@ impl Renderer {
self.debug_renderer.set_lines(&scene.drawing_context.lines);
render_data.statistics += self.debug_renderer.render(
&mut self.uniform_buffer_cache,
viewport,
observer.viewport,
&render_data.ldr_scene_framebuffer,
camera.view_projection_matrix(),
observer.position.view_projection_matrix,
)?;
for render_pass in self.scene_render_passes.iter() {
@@ -1763,9 +1766,8 @@ impl Renderer {
shader_cache: &mut self.shader_cache,
quality_settings: &self.quality_settings,
bundle_storage: &bundle_storage,
viewport,
scene,
camera,
observer,
scene_handle,
fallback_resources: &self.fallback_resources,
depth_texture: render_data.gbuffer.depth(),
+19 -17
View File
@@ -18,6 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use crate::renderer::bundle::Observer;
use crate::renderer::DynamicSurfaceCache;
use crate::{
core::{
@@ -27,7 +28,7 @@ use crate::{
},
renderer::{
bundle::{
BundleRenderContext, LightSource, LightSourceKind, ObserverInfo,
BundleRenderContext, LightSource, LightSourceKind, ObserverPosition,
RenderDataBundleStorage, RenderDataBundleStorageOptions,
},
cache::{
@@ -43,7 +44,6 @@ use crate::{
FallbackResources, RenderPassStatistics, ShadowMapPrecision, DIRECTIONAL_SHADOW_PASS_NAME,
},
scene::{
camera::Camera,
graph::Graph,
light::directional::{FrustumSplitOptions, CSM_NUM_CASCADES},
},
@@ -103,7 +103,7 @@ pub(crate) struct CsmRenderContext<'a, 'c> {
pub state: &'a dyn GraphicsServer,
pub graph: &'c Graph,
pub light: &'c LightSource,
pub camera: &'c Camera,
pub observer: &'a Observer,
pub geom_cache: &'a mut GeometryCache,
pub shader_cache: &'a mut ShaderCache,
pub texture_cache: &'a mut TextureCache,
@@ -153,7 +153,7 @@ impl CsmRenderer {
state,
graph,
light,
camera,
observer,
geom_cache,
shader_cache,
texture_cache,
@@ -178,16 +178,16 @@ impl CsmRenderer {
let z_values = match csm_options.split_options {
FrustumSplitOptions::Absolute { far_planes } => [
camera.projection().z_near(),
observer.position.z_near,
far_planes[0],
far_planes[1],
far_planes[2],
],
FrustumSplitOptions::Relative { fractions } => [
camera.projection().z_near(),
camera.projection().z_far() * fractions[0],
camera.projection().z_far() * fractions[1],
camera.projection().z_far() * fractions[2],
observer.position.z_near,
observer.position.z_far * fractions[0],
observer.position.z_far * fractions[1],
observer.position.z_far * fractions[2],
],
};
@@ -202,16 +202,17 @@ impl CsmRenderer {
z_far += MIN_DEPTH_DELTA * z_near;
}
let projection_matrix = camera
.projection()
let projection_matrix = observer
.projection
.clone()
.with_z_near(z_near)
.with_z_far(z_far)
.matrix(frame_size);
let frustum =
Frustum::from_view_projection_matrix(projection_matrix * camera.view_matrix())
.unwrap_or_default();
let frustum = Frustum::from_view_projection_matrix(
projection_matrix * observer.position.view_matrix,
)
.unwrap_or_default();
let center = frustum.center();
let observer_position = center + light_direction;
@@ -256,14 +257,15 @@ impl CsmRenderer {
let bundle_storage = RenderDataBundleStorage::from_graph(
graph,
*camera.render_mask,
observer.render_mask,
elapsed_time,
ObserverInfo {
observer_position,
&ObserverPosition {
translation: observer_position,
z_near,
z_far,
view_matrix: light_view_matrix,
projection_matrix: cascade_projection_matrix,
view_projection_matrix: cascade_projection_matrix * light_view_matrix,
},
DIRECTIONAL_SHADOW_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
+4 -3
View File
@@ -26,7 +26,7 @@ use crate::{
},
renderer::{
bundle::{
BundleRenderContext, ObserverInfo, RenderDataBundleStorage,
BundleRenderContext, ObserverPosition, RenderDataBundleStorage,
RenderDataBundleStorageOptions,
},
cache::{shader::ShaderCache, texture::TextureCache, uniform::UniformMemoryAllocator},
@@ -217,12 +217,13 @@ impl PointShadowMapRenderer {
graph,
render_mask,
elapsed_time,
ObserverInfo {
observer_position: light_pos,
&ObserverPosition {
translation: light_pos,
z_near,
z_far,
view_matrix: light_view_matrix,
projection_matrix: light_projection_matrix,
view_projection_matrix: light_projection_matrix * light_view_matrix,
},
POINT_SHADOW_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
+4 -3
View File
@@ -28,7 +28,7 @@ use crate::{
},
renderer::{
bundle::{
BundleRenderContext, ObserverInfo, RenderDataBundleStorage,
BundleRenderContext, ObserverPosition, RenderDataBundleStorage,
RenderDataBundleStorageOptions,
},
cache::{shader::ShaderCache, texture::TextureCache, uniform::UniformMemoryAllocator},
@@ -146,12 +146,13 @@ impl SpotShadowMapRenderer {
graph,
render_mask,
elapsed_time,
ObserverInfo {
observer_position: light_position,
&ObserverPosition {
translation: light_position,
z_near,
z_far,
view_matrix: light_view_matrix,
projection_matrix: light_projection_matrix,
view_projection_matrix: light_projection_matrix * light_view_matrix,
},
SPOT_SHADOW_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
+1 -1
View File
@@ -180,7 +180,7 @@ impl BatchContainer {
descendant.collect_render_data(&mut RenderContext {
render_mask: ctx.render_mask,
elapsed_time: ctx.elapsed_time,
observer_info: ctx.observer_info,
observer_position: ctx.observer_position,
frustum: None,
storage: self,
graph: ctx.graph,
+3 -3
View File
@@ -588,7 +588,7 @@ impl NodeTrait for ParticleSystem {
fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
if !self.should_be_rendered(ctx.frustum, ctx.render_mask)
|| self.is_distance_clipped(&ctx.observer_info.observer_position)
|| self.is_distance_clipped(&ctx.observer_position.translation)
{
return RdcControlFlow::Continue;
}
@@ -598,8 +598,8 @@ impl NodeTrait for ParticleSystem {
}
let distance_to_observer = ctx
.observer_info
.observer_position
.translation
.metric_distance(&self.global_position());
let particle_alpha_factor = if distance_to_observer >= self.visible_distance() {
@@ -614,7 +614,7 @@ impl NodeTrait for ParticleSystem {
let actual_position = particle.position + self.base.global_position();
particle
.sqr_distance_to_camera
.set((ctx.observer_info.observer_position - actual_position).norm_squared());
.set((ctx.observer_position.translation - actual_position).norm_squared());
sorted_particles.push(i as u32);
}
}
+2 -2
View File
@@ -2650,7 +2650,7 @@ impl NodeTrait for Terrain {
let quad_tree = chunk.quad_tree.lock();
let levels = (0..=quad_tree.max_level)
.map(|n| {
ctx.observer_info.z_far
ctx.observer_position.z_far
* ((quad_tree.max_level - n) as f32 / quad_tree.max_level as f32)
.powf(3.0)
})
@@ -2669,7 +2669,7 @@ impl NodeTrait for Terrain {
self.height_map_size(),
self.chunk_size(),
ctx.frustum,
ctx.observer_info.observer_position,
ctx.observer_position.translation,
&levels,
&mut selection,
);