Implement basic clustered decal projectors.#17315
Implement basic clustered decal projectors.#17315alice-i-cecile merged 18 commits intobevyengine:mainfrom
Conversation
This commit adds support for *decal projectors* to Bevy, allowing for textures to be projected on top of geometry. Decal projectors are clusterable objects, just as punctual lights and light probes are. This means that decals are only evaluated for objects within the conservative bounds of the projector, and they don't require a second pass. These clustered decals require support for bindless textures and as such currently don't work on WebGL 2, WebGPU, macOS, or iOS. For an alternative that doesn't require bindless, see PR bevyengine#16600. I believe that both contact projective decals in bevyengine#16600 and clustered decals are desirable to have in Bevy. Contact projective decals offer broader hardware and driver support, while clustered decals don't require the creation of bounding geometry. A new example, `decal_projectors`, has been added, which demonstrates multiple decals on a rotating object. The decal projectors can be scaled and rotated with the mouse. There are several limitations of this initial patch that can be addressed in follow-ups: 1. There's no way to specify the Z-index of decals. That is, the order in which multiple decals are blended on top of one another is arbitrary. A follow-up could introduce some sort of Z-index field so that artists can specify that some decals should be blended on top of others. 2. Decals don't take the normal of the surface they're projected onto into account. Most decal implementations in other engines have a feature whereby the angle between the decal projector and the normal of the surface must be within some threshold for the decal to appear. Often, artists can specify a fade-off range for a smooth transition between oblique surfaces and aligned surfaces. 3. There's no distance-based fadeoff toward the end of the projector range. Many decal implementations have this.
|
The generated |
JMS55
left a comment
There was a problem hiding this comment.
Generally looks fine. Two requests:
- Can you unify the naming a little more? In some places they're called clustered decals, othertimes projected. I'd rather stick with clustered everywhere.
- Won't block on it, but I would like to have one of the decals in the example be a custom material, so that users can see how to do that. I'm not sure myself, I think you'd need a switch statement on the decal type for any material on a surface that could receive a decal? Not sure.
| } | ||
|
|
||
| // TODO: this should be "if supports_bindless" or something | ||
| if binding_arrays_are_usable(&render_device, &render_adapter) { |
There was a problem hiding this comment.
I don't know how expensive this check is, maybe pull out of the loop?
There was a problem hiding this comment.
I removed the out-of-date comment. It should already be pulled out of the loop.
| ) -> vec4<f32> { | ||
| var base_color = initial_base_color; | ||
|
|
||
| #ifdef CLUSTERED_DECALS_ARE_USABLE |
There was a problem hiding this comment.
| #ifdef CLUSTERED_DECALS_ARE_USABLE | |
| #ifdef CLUSTERED_DECALS_ARE_USABLE |
Is this supposed to be called that? Do you mean to call it "CLUSTERED_DECALS_USED_IN_SCENE" or something?
There was a problem hiding this comment.
Yeah, it's supposed to be called CLUSTERED_DECALS_ARE_USABLE; it's #define'd based on whether bindless is available.
There was a problem hiding this comment.
HAS_CLUSTERED_DECALS or WITH_CLUSTERED_DECALS are more common
|
@JMS55 Do you still think a projector should be called a |
|
Merging #16600 first since it's ready; we can merge the examples together into one here :) |
|
@JMS55 I updated this with a couple of important changes:
|
| /// The maximum number of decals that can be present in a view. | ||
| /// | ||
| /// This number is currently relatively low in order to work around the lack of | ||
| /// first-class binding arrays in `wgpu`. When that feature is implemented, this |
There was a problem hiding this comment.
Is there a tracking issue for this?
There was a problem hiding this comment.
There's gfx-rs/wgpu#3637, is that what you mean?
| //! | ||
| //! Clustered decals are the highest-quality types of decals that Bevy supports, | ||
| //! but they require bindless textures. This means that they presently can't be | ||
| //! used on WebGL 2, WebGPU, macOS, or iOS. Bevy's clustered decals can be used |
There was a problem hiding this comment.
The example seems to work on my m1 mac?
Fails as expected on WebGL and WebGPU, but with sort of mysterious errors. Not sure if this is expected or if the bindless gating isn't working properly.
(This text is repeated for ClusteredDecal docs, so any rework should be repeated there)
// webgl2
ERROR crates/bevy_render/src/render_resource/pipeline_cache.rs:895 failed to process shader:error: no definition in scope for identifier: 'bevy_pbr::decal::clustered::clustered_decal_iterator_new'
/// webgpu displays the same error, but also has various complains.
[Invalid ShaderModule (unlabeled)] is invalid.
- While validating compute stage ([Invalid ShaderModule (unlabeled)], entryPoint: "main").
- While calling [Device].CreateComputePipeline([ComputePipelineDescriptor ""build indexed indirect parameters""]).
Understand this warningAI
127.0.0.1/:1 Compilation log for [Invalid ShaderModule (unlabeled)]:
1 error(s) generated while compiling the shader:
:36:1 error: atomic variables in 'storage' address space must have 'read_write' access mode
var<storage> indirect_parameters_metadata: array<IndirectParametersMetadataX_naga_oil_mod_XMJSXM6K7OBRHEOR2NVSXG2C7OBZGK4DSN5RWK43TL52HS4DFOMX>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:25:5 note: atomic sub-type of 'array<IndirectParametersMetadataX_naga_oil_mod_XMJSXM6K7OBRHEOR2NVSXG2C7OBZGK4DSN5RWK43TL52HS4DFOMX>' is declared here
instance_count: atomic<u32>,
^^^^^^^^^^^^^^
There was a problem hiding this comment.
For WebGL, that's expected. I don't think there's much point in gating off decals with an ifdef in the example's custom shader if the example requires a platform that decals don't work on to begin with.
For WebGPU, the indirect parameters metadata error is unrelated and I'd rather not fix that here.
There was a problem hiding this comment.
Good point on the macOS/iOS thing. They don't have true bindless, but I forget that wgpu has a partial workaround that's good enough to make clustered decals work. I removed the mention of those platforms.
There was a problem hiding this comment.
The error means some bound buffer is marked read-only but contains some atomic. That's not allowed by WGSL. In Hanabi I use some #ifdef to change the definition of the atomic fields to non-aromic and read them as non-atomic where it's safe to do so, to avoid that error. That's a bit tedious but it's clean.
| // | ||
| // Returns true if another decal was found or false if no more decals were found | ||
| // for this position. | ||
| fn clustered_decal_iterator_next(iterator: ptr<function, ClusteredDecalIterator>) -> bool { |
There was a problem hiding this comment.
cool pattern, hadn't seen this before
|
Example runner looks good! https://pixel-eagle.com/project/B25A040A-A980-4602-B90C-D480AB84076D/run/7475/compare/7463 |
This commit adds support for *decal projectors* to Bevy, allowing for textures to be projected on top of geometry. Decal projectors are clusterable objects, just as punctual lights and light probes are. This means that decals are only evaluated for objects within the conservative bounds of the projector, and they don't require a second pass. These clustered decals require support for bindless textures and as such currently don't work on WebGL 2, WebGPU, macOS, or iOS. For an alternative that doesn't require bindless, see PR bevyengine#16600. I believe that both contact projective decals in bevyengine#16600 and clustered decals are desirable to have in Bevy. Contact projective decals offer broader hardware and driver support, while clustered decals don't require the creation of bounding geometry. A new example, `decal_projectors`, has been added, which demonstrates multiple decals on a rotating object. The decal projectors can be scaled and rotated with the mouse. There are several limitations of this initial patch that can be addressed in follow-ups: 1. There's no way to specify the Z-index of decals. That is, the order in which multiple decals are blended on top of one another is arbitrary. A follow-up could introduce some sort of Z-index field so that artists can specify that some decals should be blended on top of others. 2. Decals don't take the normal of the surface they're projected onto into account. Most decal implementations in other engines have a feature whereby the angle between the decal projector and the normal of the surface must be within some threshold for the decal to appear. Often, artists can specify a fade-off range for a smooth transition between oblique surfaces and aligned surfaces. 3. There's no distance-based fadeoff toward the end of the projector range. Many decal implementations have this. This addresses bevyengine#2401. ## Showcase 
|
Thank you to everyone involved with the authoring or reviewing of this PR! This work is relatively important and needs release notes! Head over to bevyengine/bevy-website#1986 if you'd like to help out. |
This commit adds support for decal projectors to Bevy, allowing for textures to be projected on top of geometry. Decal projectors are clusterable objects, just as punctual lights and light probes are. This means that decals are only evaluated for objects within the conservative bounds of the projector, and they don't require a second pass.
These clustered decals require support for bindless textures and as such currently don't work on WebGL 2, WebGPU, macOS, or iOS. For an alternative that doesn't require bindless, see PR #16600. I believe that both contact projective decals in #16600 and clustered decals are desirable to have in Bevy. Contact projective decals offer broader hardware and driver support, while clustered decals don't require the creation of bounding geometry.
A new example,
decal_projectors, has been added, which demonstrates multiple decals on a rotating object. The decal projectors can be scaled and rotated with the mouse.There are several limitations of this initial patch that can be addressed in follow-ups:
There's no way to specify the Z-index of decals. That is, the order in which multiple decals are blended on top of one another is arbitrary. A follow-up could introduce some sort of Z-index field so that artists can specify that some decals should be blended on top of others.
Decals don't take the normal of the surface they're projected onto into account. Most decal implementations in other engines have a feature whereby the angle between the decal projector and the normal of the surface must be within some threshold for the decal to appear. Often, artists can specify a fade-off range for a smooth transition between oblique surfaces and aligned surfaces.
There's no distance-based fadeoff toward the end of the projector range. Many decal implementations have this.
This addresses #2401.
Showcase