Add Skybox Motion Vectors (#13617)
# Objective - Add motion vector support to the skybox - This fixes the last remaining "gap" to complete the motion blur feature ## Solution - Add a pipeline for the skybox to write motion vectors to the prepass ## Testing - Used examples to test motion vectors using motion blur https://github.com/bevyengine/bevy/assets/2632925/74c0778a-7e77-4e68-8111-05791e4bfdd2 --------- Co-authored-by: Patrick Walton <pcwalton@mimiga.net>
This commit is contained in:
parent
7d3fcd5067
commit
b45786df41
@ -31,6 +31,7 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use bevy_asset::AssetId;
|
use bevy_asset::AssetId;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_math::Mat4;
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
mesh::Mesh,
|
mesh::Mesh,
|
||||||
@ -38,10 +39,15 @@ use bevy_render::{
|
|||||||
BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem,
|
BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem,
|
||||||
PhaseItemExtraIndex,
|
PhaseItemExtraIndex,
|
||||||
},
|
},
|
||||||
render_resource::{BindGroupId, CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
|
render_resource::{
|
||||||
|
BindGroupId, CachedRenderPipelineId, ColorTargetState, ColorWrites, DynamicUniformBuffer,
|
||||||
|
Extent3d, ShaderType, TextureFormat, TextureView,
|
||||||
|
},
|
||||||
texture::ColorAttachment,
|
texture::ColorAttachment,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::deferred::{DEFERRED_LIGHTING_PASS_ID_FORMAT, DEFERRED_PREPASS_FORMAT};
|
||||||
|
|
||||||
pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
|
pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
|
||||||
pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
|
pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
|
||||||
|
|
||||||
@ -63,6 +69,22 @@ pub struct MotionVectorPrepass;
|
|||||||
#[derive(Component, Default, Reflect)]
|
#[derive(Component, Default, Reflect)]
|
||||||
pub struct DeferredPrepass;
|
pub struct DeferredPrepass;
|
||||||
|
|
||||||
|
#[derive(Component, ShaderType, Clone)]
|
||||||
|
pub struct PreviousViewData {
|
||||||
|
pub inverse_view: Mat4,
|
||||||
|
pub view_proj: Mat4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct PreviousViewUniforms {
|
||||||
|
pub uniforms: DynamicUniformBuffer<PreviousViewData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct PreviousViewUniformOffset {
|
||||||
|
pub offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Textures that are written to by the prepass.
|
/// Textures that are written to by the prepass.
|
||||||
///
|
///
|
||||||
/// This component will only be present if any of the relevant prepass components are also present.
|
/// This component will only be present if any of the relevant prepass components are also present.
|
||||||
@ -270,3 +292,32 @@ impl CachedRenderPipelinePhaseItem for AlphaMask3dPrepass {
|
|||||||
self.key.pipeline
|
self.key.pipeline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prepass_target_descriptors(
|
||||||
|
normal_prepass: bool,
|
||||||
|
motion_vector_prepass: bool,
|
||||||
|
deferred_prepass: bool,
|
||||||
|
) -> Vec<Option<ColorTargetState>> {
|
||||||
|
vec![
|
||||||
|
normal_prepass.then_some(ColorTargetState {
|
||||||
|
format: NORMAL_PREPASS_FORMAT,
|
||||||
|
blend: None,
|
||||||
|
write_mask: ColorWrites::ALL,
|
||||||
|
}),
|
||||||
|
motion_vector_prepass.then_some(ColorTargetState {
|
||||||
|
format: MOTION_VECTOR_PREPASS_FORMAT,
|
||||||
|
blend: None,
|
||||||
|
write_mask: ColorWrites::ALL,
|
||||||
|
}),
|
||||||
|
deferred_prepass.then_some(ColorTargetState {
|
||||||
|
format: DEFERRED_PREPASS_FORMAT,
|
||||||
|
blend: None,
|
||||||
|
write_mask: ColorWrites::ALL,
|
||||||
|
}),
|
||||||
|
deferred_prepass.then_some(ColorTargetState {
|
||||||
|
format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
|
||||||
|
blend: None,
|
||||||
|
write_mask: ColorWrites::ALL,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@ -5,14 +5,19 @@ use bevy_render::{
|
|||||||
diagnostic::RecordDiagnostics,
|
diagnostic::RecordDiagnostics,
|
||||||
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
||||||
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
|
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
|
||||||
render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
|
render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
|
||||||
renderer::RenderContext,
|
renderer::RenderContext,
|
||||||
view::ViewDepthTexture,
|
view::{ViewDepthTexture, ViewUniformOffset},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
use bevy_utils::tracing::info_span;
|
use bevy_utils::tracing::info_span;
|
||||||
|
|
||||||
use super::{AlphaMask3dPrepass, DeferredPrepass, Opaque3dPrepass, ViewPrepassTextures};
|
use crate::skybox::prepass::{RenderSkyboxPrepassPipeline, SkyboxPrepassBindGroup};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
AlphaMask3dPrepass, DeferredPrepass, Opaque3dPrepass, PreviousViewUniformOffset,
|
||||||
|
ViewPrepassTextures,
|
||||||
|
};
|
||||||
|
|
||||||
/// Render node used by the prepass.
|
/// Render node used by the prepass.
|
||||||
///
|
///
|
||||||
@ -26,17 +31,28 @@ impl ViewNode for PrepassNode {
|
|||||||
&'static ExtractedCamera,
|
&'static ExtractedCamera,
|
||||||
&'static ViewDepthTexture,
|
&'static ViewDepthTexture,
|
||||||
&'static ViewPrepassTextures,
|
&'static ViewPrepassTextures,
|
||||||
|
&'static ViewUniformOffset,
|
||||||
Option<&'static DeferredPrepass>,
|
Option<&'static DeferredPrepass>,
|
||||||
|
Option<&'static RenderSkyboxPrepassPipeline>,
|
||||||
|
Option<&'static SkyboxPrepassBindGroup>,
|
||||||
|
Option<&'static PreviousViewUniformOffset>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run<'w>(
|
fn run<'w>(
|
||||||
&self,
|
&self,
|
||||||
graph: &mut RenderGraphContext,
|
graph: &mut RenderGraphContext,
|
||||||
render_context: &mut RenderContext<'w>,
|
render_context: &mut RenderContext<'w>,
|
||||||
(view, camera, view_depth_texture, view_prepass_textures, deferred_prepass): QueryItem<
|
(
|
||||||
'w,
|
view,
|
||||||
Self::ViewQuery,
|
camera,
|
||||||
>,
|
view_depth_texture,
|
||||||
|
view_prepass_textures,
|
||||||
|
view_uniform_offset,
|
||||||
|
deferred_prepass,
|
||||||
|
skybox_prepass_pipeline,
|
||||||
|
skybox_prepass_bind_group,
|
||||||
|
view_prev_uniform_offset,
|
||||||
|
): QueryItem<'w, Self::ViewQuery>,
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
) -> Result<(), NodeRunError> {
|
) -> Result<(), NodeRunError> {
|
||||||
let (Some(opaque_prepass_phases), Some(alpha_mask_prepass_phases)) = (
|
let (Some(opaque_prepass_phases), Some(alpha_mask_prepass_phases)) = (
|
||||||
@ -119,6 +135,30 @@ impl ViewNode for PrepassNode {
|
|||||||
alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity);
|
alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skybox draw using a fullscreen triangle
|
||||||
|
if let (
|
||||||
|
Some(skybox_prepass_pipeline),
|
||||||
|
Some(skybox_prepass_bind_group),
|
||||||
|
Some(view_prev_uniform_offset),
|
||||||
|
) = (
|
||||||
|
skybox_prepass_pipeline,
|
||||||
|
skybox_prepass_bind_group,
|
||||||
|
view_prev_uniform_offset,
|
||||||
|
) {
|
||||||
|
let pipeline_cache = world.resource::<PipelineCache>();
|
||||||
|
if let Some(pipeline) =
|
||||||
|
pipeline_cache.get_render_pipeline(skybox_prepass_pipeline.0)
|
||||||
|
{
|
||||||
|
render_pass.set_render_pipeline(pipeline);
|
||||||
|
render_pass.set_bind_group(
|
||||||
|
0,
|
||||||
|
&skybox_prepass_bind_group.0,
|
||||||
|
&[view_uniform_offset.offset, view_prev_uniform_offset.offset],
|
||||||
|
);
|
||||||
|
render_pass.draw(0..3, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pass_span.end(&mut render_pass);
|
pass_span.end(&mut render_pass);
|
||||||
drop(render_pass);
|
drop(render_pass);
|
||||||
|
|
||||||
|
|||||||
@ -22,16 +22,25 @@ use bevy_render::{
|
|||||||
view::{ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniforms},
|
view::{ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniforms},
|
||||||
Render, RenderApp, RenderSet,
|
Render, RenderApp, RenderSet,
|
||||||
};
|
};
|
||||||
|
use prepass::{SkyboxPrepassPipeline, SKYBOX_PREPASS_SHADER_HANDLE};
|
||||||
|
|
||||||
use crate::core_3d::CORE_3D_DEPTH_FORMAT;
|
use crate::core_3d::CORE_3D_DEPTH_FORMAT;
|
||||||
|
|
||||||
const SKYBOX_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(55594763423201);
|
const SKYBOX_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(55594763423201);
|
||||||
|
|
||||||
|
pub mod prepass;
|
||||||
|
|
||||||
pub struct SkyboxPlugin;
|
pub struct SkyboxPlugin;
|
||||||
|
|
||||||
impl Plugin for SkyboxPlugin {
|
impl Plugin for SkyboxPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
load_internal_asset!(app, SKYBOX_SHADER_HANDLE, "skybox.wgsl", Shader::from_wgsl);
|
load_internal_asset!(app, SKYBOX_SHADER_HANDLE, "skybox.wgsl", Shader::from_wgsl);
|
||||||
|
load_internal_asset!(
|
||||||
|
app,
|
||||||
|
SKYBOX_PREPASS_SHADER_HANDLE,
|
||||||
|
"skybox_prepass.wgsl",
|
||||||
|
Shader::from_wgsl
|
||||||
|
);
|
||||||
|
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
ExtractComponentPlugin::<Skybox>::default(),
|
ExtractComponentPlugin::<Skybox>::default(),
|
||||||
@ -43,11 +52,15 @@ impl Plugin for SkyboxPlugin {
|
|||||||
};
|
};
|
||||||
render_app
|
render_app
|
||||||
.init_resource::<SpecializedRenderPipelines<SkyboxPipeline>>()
|
.init_resource::<SpecializedRenderPipelines<SkyboxPipeline>>()
|
||||||
|
.init_resource::<SpecializedRenderPipelines<SkyboxPrepassPipeline>>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Render,
|
Render,
|
||||||
(
|
(
|
||||||
prepare_skybox_pipelines.in_set(RenderSet::Prepare),
|
prepare_skybox_pipelines.in_set(RenderSet::Prepare),
|
||||||
|
prepass::prepare_skybox_prepass_pipelines.in_set(RenderSet::Prepare),
|
||||||
prepare_skybox_bind_groups.in_set(RenderSet::PrepareBindGroups),
|
prepare_skybox_bind_groups.in_set(RenderSet::PrepareBindGroups),
|
||||||
|
prepass::prepare_skybox_prepass_bind_groups
|
||||||
|
.in_set(RenderSet::PrepareBindGroups),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -57,7 +70,9 @@ impl Plugin for SkyboxPlugin {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let render_device = render_app.world().resource::<RenderDevice>().clone();
|
let render_device = render_app.world().resource::<RenderDevice>().clone();
|
||||||
render_app.insert_resource(SkyboxPipeline::new(&render_device));
|
render_app
|
||||||
|
.insert_resource(SkyboxPipeline::new(&render_device))
|
||||||
|
.init_resource::<SkyboxPrepassPipeline>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
165
crates/bevy_core_pipeline/src/skybox/prepass.rs
Normal file
165
crates/bevy_core_pipeline/src/skybox/prepass.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
//! Adds motion vector support to skyboxes. See [`SkyboxPrepassPipeline`] for details.
|
||||||
|
|
||||||
|
use bevy_asset::Handle;
|
||||||
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
|
entity::Entity,
|
||||||
|
query::{Has, With},
|
||||||
|
system::{Commands, Query, Res, ResMut, Resource},
|
||||||
|
world::{FromWorld, World},
|
||||||
|
};
|
||||||
|
use bevy_render::{
|
||||||
|
render_resource::{
|
||||||
|
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayout,
|
||||||
|
BindGroupLayoutEntries, CachedRenderPipelineId, CompareFunction, DepthStencilState,
|
||||||
|
FragmentState, MultisampleState, PipelineCache, RenderPipelineDescriptor, Shader,
|
||||||
|
ShaderStages, SpecializedRenderPipeline, SpecializedRenderPipelines,
|
||||||
|
},
|
||||||
|
renderer::RenderDevice,
|
||||||
|
view::{Msaa, ViewUniform, ViewUniforms},
|
||||||
|
};
|
||||||
|
use bevy_utils::prelude::default;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core_3d::CORE_3D_DEPTH_FORMAT,
|
||||||
|
prepass::{
|
||||||
|
prepass_target_descriptors, MotionVectorPrepass, NormalPrepass, PreviousViewData,
|
||||||
|
PreviousViewUniforms,
|
||||||
|
},
|
||||||
|
Skybox,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SKYBOX_PREPASS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(376510055324461154);
|
||||||
|
|
||||||
|
/// This pipeline writes motion vectors to the prepass for all [`Skybox`]es.
|
||||||
|
///
|
||||||
|
/// This allows features like motion blur and TAA to work correctly on the skybox. Without this, for
|
||||||
|
/// example, motion blur would not be applied to the skybox when the camera is rotated and motion
|
||||||
|
/// blur is enabled.
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct SkyboxPrepassPipeline {
|
||||||
|
bind_group_layout: BindGroupLayout,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to specialize the [`SkyboxPrepassPipeline`].
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub struct SkyboxPrepassPipelineKey {
|
||||||
|
samples: u32,
|
||||||
|
normal_prepass: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores the ID for a camera's specialized pipeline, so it can be retrieved from the
|
||||||
|
/// [`PipelineCache`].
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct RenderSkyboxPrepassPipeline(pub CachedRenderPipelineId);
|
||||||
|
|
||||||
|
/// Stores the [`SkyboxPrepassPipeline`] bind group for a camera. This is later used by the prepass
|
||||||
|
/// render graph node to add this binding to the prepass's render pass.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct SkyboxPrepassBindGroup(pub BindGroup);
|
||||||
|
|
||||||
|
impl FromWorld for SkyboxPrepassPipeline {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let render_device = world.resource::<RenderDevice>();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
bind_group_layout: render_device.create_bind_group_layout(
|
||||||
|
"skybox_prepass_bind_group_layout",
|
||||||
|
&BindGroupLayoutEntries::sequential(
|
||||||
|
ShaderStages::FRAGMENT,
|
||||||
|
(
|
||||||
|
uniform_buffer::<ViewUniform>(true),
|
||||||
|
uniform_buffer::<PreviousViewData>(true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecializedRenderPipeline for SkyboxPrepassPipeline {
|
||||||
|
type Key = SkyboxPrepassPipelineKey;
|
||||||
|
|
||||||
|
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
||||||
|
RenderPipelineDescriptor {
|
||||||
|
label: Some("skybox_prepass_pipeline".into()),
|
||||||
|
layout: vec![self.bind_group_layout.clone()],
|
||||||
|
push_constant_ranges: vec![],
|
||||||
|
vertex: crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state(),
|
||||||
|
primitive: default(),
|
||||||
|
depth_stencil: Some(DepthStencilState {
|
||||||
|
format: CORE_3D_DEPTH_FORMAT,
|
||||||
|
depth_write_enabled: false,
|
||||||
|
depth_compare: CompareFunction::GreaterEqual,
|
||||||
|
stencil: default(),
|
||||||
|
bias: default(),
|
||||||
|
}),
|
||||||
|
multisample: MultisampleState {
|
||||||
|
count: key.samples,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
fragment: Some(FragmentState {
|
||||||
|
shader: SKYBOX_PREPASS_SHADER_HANDLE,
|
||||||
|
shader_defs: vec![],
|
||||||
|
entry_point: "fragment".into(),
|
||||||
|
targets: prepass_target_descriptors(key.normal_prepass, true, false),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specialize and cache the [`SkyboxPrepassPipeline`] for each camera with a [`Skybox`].
|
||||||
|
pub fn prepare_skybox_prepass_pipelines(
|
||||||
|
mut commands: Commands,
|
||||||
|
pipeline_cache: Res<PipelineCache>,
|
||||||
|
mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPrepassPipeline>>,
|
||||||
|
msaa: Res<Msaa>,
|
||||||
|
pipeline: Res<SkyboxPrepassPipeline>,
|
||||||
|
views: Query<(Entity, Has<NormalPrepass>), (With<Skybox>, With<MotionVectorPrepass>)>,
|
||||||
|
) {
|
||||||
|
for (entity, normal_prepass) in &views {
|
||||||
|
let pipeline_key = SkyboxPrepassPipelineKey {
|
||||||
|
samples: msaa.samples(),
|
||||||
|
normal_prepass,
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_skybox_prepass_pipeline =
|
||||||
|
pipelines.specialize(&pipeline_cache, &pipeline, pipeline_key);
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.insert(RenderSkyboxPrepassPipeline(render_skybox_prepass_pipeline));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the required bind groups for the [`SkyboxPrepassPipeline`]. This binds the view uniforms
|
||||||
|
/// from the CPU for access in the prepass shader on the GPU, allowing us to compute camera motion
|
||||||
|
/// between frames. This is then stored in the [`SkyboxPrepassBindGroup`] component on the camera.
|
||||||
|
pub fn prepare_skybox_prepass_bind_groups(
|
||||||
|
mut commands: Commands,
|
||||||
|
pipeline: Res<SkyboxPrepassPipeline>,
|
||||||
|
view_uniforms: Res<ViewUniforms>,
|
||||||
|
prev_view_uniforms: Res<PreviousViewUniforms>,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
views: Query<Entity, (With<Skybox>, With<MotionVectorPrepass>)>,
|
||||||
|
) {
|
||||||
|
for entity in &views {
|
||||||
|
let (Some(view_uniforms), Some(prev_view_uniforms)) = (
|
||||||
|
view_uniforms.uniforms.binding(),
|
||||||
|
prev_view_uniforms.uniforms.binding(),
|
||||||
|
) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let bind_group = render_device.create_bind_group(
|
||||||
|
"skybox_prepass_bind_group",
|
||||||
|
&pipeline.bind_group_layout,
|
||||||
|
&BindGroupEntries::sequential((view_uniforms, prev_view_uniforms)),
|
||||||
|
);
|
||||||
|
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.insert(SkyboxPrepassBindGroup(bind_group));
|
||||||
|
}
|
||||||
|
}
|
||||||
21
crates/bevy_core_pipeline/src/skybox/skybox_prepass.wgsl
Normal file
21
crates/bevy_core_pipeline/src/skybox/skybox_prepass.wgsl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#import bevy_render::view::View
|
||||||
|
#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput
|
||||||
|
#import bevy_pbr::view_transformations::uv_to_ndc
|
||||||
|
|
||||||
|
struct PreviousViewUniforms {
|
||||||
|
inverse_view: mat4x4<f32>,
|
||||||
|
view_proj: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> view: View;
|
||||||
|
@group(0) @binding(1) var<uniform> previous_view: PreviousViewUniforms;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fragment(in: FullscreenVertexOutput) -> @location(1) vec4<f32> {
|
||||||
|
let clip_pos = uv_to_ndc(in.uv); // Convert from uv to clip space
|
||||||
|
let world_pos = view.inverse_view_proj * vec4(clip_pos, 0.0, 1.0);
|
||||||
|
let prev_clip_pos = (previous_view.view_proj * world_pos).xy;
|
||||||
|
let velocity = (clip_pos - prev_clip_pos) * vec2(0.5, -0.5); // Copied from mesh motion vectors
|
||||||
|
|
||||||
|
return vec4(velocity.x, velocity.y, 0.0, 1.0);
|
||||||
|
}
|
||||||
@ -4,11 +4,13 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Material, MeshFlags, MeshTransforms, MeshUniform, NotShadowCaster, NotShadowReceiver,
|
Material, MeshFlags, MeshTransforms, MeshUniform, NotShadowCaster, NotShadowReceiver,
|
||||||
PreviousGlobalTransform, PreviousViewData, PreviousViewUniforms, RenderMaterialInstances,
|
PreviousGlobalTransform, RenderMaterialInstances, ShadowView,
|
||||||
ShadowView,
|
|
||||||
};
|
};
|
||||||
use bevy_asset::{AssetEvent, AssetId, AssetServer, Assets, Handle, UntypedAssetId};
|
use bevy_asset::{AssetEvent, AssetId, AssetServer, Assets, Handle, UntypedAssetId};
|
||||||
use bevy_core_pipeline::core_3d::Camera3d;
|
use bevy_core_pipeline::{
|
||||||
|
core_3d::Camera3d,
|
||||||
|
prepass::{PreviousViewData, PreviousViewUniforms},
|
||||||
|
};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::{Entity, EntityHashMap},
|
entity::{Entity, EntityHashMap},
|
||||||
|
|||||||
@ -7,10 +7,10 @@ use super::{
|
|||||||
MeshletGpuScene,
|
MeshletGpuScene,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
MeshViewBindGroup, PrepassViewBindGroup, PreviousViewUniformOffset, ViewFogUniformOffset,
|
MeshViewBindGroup, PrepassViewBindGroup, ViewFogUniformOffset, ViewLightProbesUniformOffset,
|
||||||
ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
|
ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
|
||||||
};
|
};
|
||||||
use bevy_core_pipeline::prepass::ViewPrepassTextures;
|
use bevy_core_pipeline::prepass::{PreviousViewUniformOffset, ViewPrepassTextures};
|
||||||
use bevy_ecs::{query::QueryItem, world::World};
|
use bevy_ecs::{query::QueryItem, world::World};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
camera::ExtractedCamera,
|
camera::ExtractedCamera,
|
||||||
|
|||||||
@ -2,8 +2,9 @@ use super::{
|
|||||||
gpu_scene::{MeshletViewBindGroups, MeshletViewResources},
|
gpu_scene::{MeshletViewBindGroups, MeshletViewResources},
|
||||||
pipelines::MeshletPipelines,
|
pipelines::MeshletPipelines,
|
||||||
};
|
};
|
||||||
use crate::{LightEntity, PreviousViewUniformOffset, ShadowView, ViewLightEntities};
|
use crate::{LightEntity, ShadowView, ViewLightEntities};
|
||||||
use bevy_color::LinearRgba;
|
use bevy_color::LinearRgba;
|
||||||
|
use bevy_core_pipeline::prepass::PreviousViewUniformOffset;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
query::QueryState,
|
query::QueryState,
|
||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use bevy_ecs::{
|
|||||||
SystemParamItem,
|
SystemParamItem,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bevy_math::{Affine3A, Mat4};
|
use bevy_math::Affine3A;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
globals::{GlobalsBuffer, GlobalsUniform},
|
globals::{GlobalsBuffer, GlobalsUniform},
|
||||||
prelude::{Camera, Mesh},
|
prelude::{Camera, Mesh},
|
||||||
@ -194,12 +194,6 @@ where
|
|||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct AnyPrepassPluginLoaded;
|
struct AnyPrepassPluginLoaded;
|
||||||
|
|
||||||
#[derive(Component, ShaderType, Clone)]
|
|
||||||
pub struct PreviousViewData {
|
|
||||||
pub inverse_view: Mat4,
|
|
||||||
pub view_proj: Mat4,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "meshlet"))]
|
#[cfg(not(feature = "meshlet"))]
|
||||||
type PreviousViewFilter = (With<Camera3d>, With<MotionVectorPrepass>);
|
type PreviousViewFilter = (With<Camera3d>, With<MotionVectorPrepass>);
|
||||||
#[cfg(feature = "meshlet")]
|
#[cfg(feature = "meshlet")]
|
||||||
@ -472,39 +466,12 @@ where
|
|||||||
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
|
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
|
||||||
|
|
||||||
// Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
|
// Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
|
||||||
let mut targets = vec![
|
let mut targets = prepass_target_descriptors(
|
||||||
|
key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS),
|
||||||
key.mesh_key
|
key.mesh_key
|
||||||
.contains(MeshPipelineKey::NORMAL_PREPASS)
|
.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS),
|
||||||
.then_some(ColorTargetState {
|
key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS),
|
||||||
format: NORMAL_PREPASS_FORMAT,
|
);
|
||||||
// BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases.
|
|
||||||
blend: None,
|
|
||||||
write_mask: ColorWrites::ALL,
|
|
||||||
}),
|
|
||||||
key.mesh_key
|
|
||||||
.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS)
|
|
||||||
.then_some(ColorTargetState {
|
|
||||||
format: MOTION_VECTOR_PREPASS_FORMAT,
|
|
||||||
// BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases.
|
|
||||||
blend: None,
|
|
||||||
write_mask: ColorWrites::ALL,
|
|
||||||
}),
|
|
||||||
key.mesh_key
|
|
||||||
.contains(MeshPipelineKey::DEFERRED_PREPASS)
|
|
||||||
.then_some(ColorTargetState {
|
|
||||||
format: DEFERRED_PREPASS_FORMAT,
|
|
||||||
// BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases.
|
|
||||||
blend: None,
|
|
||||||
write_mask: ColorWrites::ALL,
|
|
||||||
}),
|
|
||||||
key.mesh_key
|
|
||||||
.contains(MeshPipelineKey::DEFERRED_PREPASS)
|
|
||||||
.then_some(ColorTargetState {
|
|
||||||
format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
|
|
||||||
blend: None,
|
|
||||||
write_mask: ColorWrites::ALL,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
if targets.iter().all(Option::is_none) {
|
if targets.iter().all(Option::is_none) {
|
||||||
// if no targets are required then clear the list, so that no fragment shader is required
|
// if no targets are required then clear the list, so that no fragment shader is required
|
||||||
@ -623,16 +590,6 @@ pub fn extract_camera_previous_view_data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
|
||||||
pub struct PreviousViewUniforms {
|
|
||||||
pub uniforms: DynamicUniformBuffer<PreviousViewData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct PreviousViewUniformOffset {
|
|
||||||
pub offset: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_previous_view_uniforms(
|
pub fn prepare_previous_view_uniforms(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user