Fix motion blur on skinned meshes (#18712)

## Objective

Fix motion blur not working on skinned meshes.

## Solution

`set_mesh_motion_vector_flags` can set
`RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN` after specialization has
already cached the material. This can lead to
`MeshPipelineKey::HAS_PREVIOUS_SKIN` never getting set, disabling motion
blur.

The fix is to make sure `set_mesh_motion_vector_flags` happens before
specialization.

Note that the bug is fixed in a different way by #18074, which includes
other fixes but is a much larger change.

## Testing

Open the `animated_mesh` example and add these components to the
`Camera3d` entity:

```rust
MotionBlur {
    shutter_angle: 5.0,
    samples: 2,
    #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))]
    _webgl2_padding: Default::default(),
},
#[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))]
Msaa::Off,
```

Tested on `animated_mesh`, `many_foxes`, `custom_skinned_mesh`,
Win10/Nvidia with Vulkan, WebGL/Chrome, WebGPU/Chrome. Note that testing
`many_foxes` WebGL requires #18715.
This commit is contained in:
Greeble 2025-04-04 23:36:03 +01:00 committed by GitHub
parent 78d5c50b50
commit 5da64ddbee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 10 additions and 8 deletions

View File

@ -327,7 +327,8 @@ where
.in_set(RenderSet::PrepareMeshes)
.after(prepare_assets::<PreparedMaterial<M>>)
.after(prepare_assets::<RenderMesh>)
.after(collect_meshes_for_gpu_building),
.after(collect_meshes_for_gpu_building)
.after(set_mesh_motion_vector_flags),
queue_material_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial<M>>),

View File

@ -3,11 +3,11 @@ mod prepass_bindings;
use crate::{
alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout,
collect_meshes_for_gpu_building, material_bind_groups::MaterialBindGroupAllocator,
queue_material_meshes, setup_morph_and_skinning_defs, skin, DrawMesh,
EntitySpecializationTicks, Material, MaterialPipeline, MaterialPipelineKey, MeshLayouts,
MeshPipeline, MeshPipelineKey, OpaqueRendererMethod, PreparedMaterial, RenderLightmaps,
RenderMaterialInstances, RenderMeshInstanceFlags, RenderMeshInstances, RenderPhaseType,
SetMaterialBindGroup, SetMeshBindGroup, ShadowView, StandardMaterial,
queue_material_meshes, set_mesh_motion_vector_flags, setup_morph_and_skinning_defs, skin,
DrawMesh, EntitySpecializationTicks, Material, MaterialPipeline, MaterialPipelineKey,
MeshLayouts, MeshPipeline, MeshPipelineKey, OpaqueRendererMethod, PreparedMaterial,
RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags, RenderMeshInstances,
RenderPhaseType, SetMaterialBindGroup, SetMeshBindGroup, ShadowView, StandardMaterial,
};
use bevy_app::{App, Plugin, PreUpdate};
use bevy_render::{
@ -219,7 +219,8 @@ where
.in_set(RenderSet::PrepareMeshes)
.after(prepare_assets::<PreparedMaterial<M>>)
.after(prepare_assets::<RenderMesh>)
.after(collect_meshes_for_gpu_building),
.after(collect_meshes_for_gpu_building)
.after(set_mesh_motion_vector_flags),
queue_prepass_material_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial<M>>)

View File

@ -1648,7 +1648,7 @@ fn extract_mesh_for_gpu_building(
/// [`crate::material::queue_material_meshes`] check the skin and morph target
/// tables for each mesh, but that would be too slow in the hot mesh queuing
/// loop.
fn set_mesh_motion_vector_flags(
pub(crate) fn set_mesh_motion_vector_flags(
mut render_mesh_instances: ResMut<RenderMeshInstances>,
skin_uniforms: Res<SkinUniforms>,
morph_indices: Res<MorphIndices>,