@@ -30,6 +30,7 @@ use bevy::{
3030 RenderApp , RenderStartup ,
3131 } ,
3232} ;
33+ use plugin:: { FullscreenMaterial , FullscreenMaterialPlugin } ;
3334
3435/// This example uses a shader source file from the assets subdirectory
3536const SHADER_ASSET_PATH : & str = "shaders/post_processing.wgsl" ;
@@ -312,6 +313,7 @@ fn setup(
312313 intensity : 0.02 ,
313314 ..default ( )
314315 } ,
316+ MyPostProcessing { data : 1.0 } ,
315317 ) ) ;
316318
317319 // cube
@@ -355,3 +357,146 @@ fn update_settings(mut settings: Query<&mut PostProcessSettings>, time: Res<Time
355357 setting. intensity = intensity;
356358 }
357359}
360+
361+ mod plugin {
362+ use std:: marker:: PhantomData ;
363+
364+ use bevy:: { core_pipeline:: FullscreenShader , prelude:: * , shader:: ShaderRef } ;
365+ use bevy_render:: {
366+ extract_component:: {
367+ DynamicUniformIndex , ExtractComponent , ExtractComponentPlugin , UniformComponentPlugin ,
368+ } ,
369+ render_graph:: ViewNode ,
370+ render_resource:: {
371+ binding_types:: { sampler, texture_2d, uniform_buffer} ,
372+ encase:: internal:: WriteInto ,
373+ AsBindGroup , BindGroupLayout , BindGroupLayoutEntries , CachedRenderPipelineId ,
374+ ColorTargetState , ColorWrites , FragmentState , PipelineCache , RenderPipelineDescriptor ,
375+ Sampler , SamplerBindingType , SamplerDescriptor , ShaderStages , ShaderType ,
376+ TextureFormat , TextureSampleType ,
377+ } ,
378+ renderer:: RenderDevice ,
379+ view:: ViewTarget ,
380+ RenderApp , RenderStartup ,
381+ } ;
382+
383+ use crate :: PostProcessSettings ;
384+
385+ #[ derive( Default ) ]
386+ pub struct FullscreenMaterialPlugin < T : FullscreenMaterial > {
387+ _marker : PhantomData < T > ,
388+ }
389+ impl < T : FullscreenMaterial > Plugin for FullscreenMaterialPlugin < T > {
390+ fn build ( & self , app : & mut App ) {
391+ app. add_plugins ( (
392+ ExtractComponentPlugin :: < T > :: default ( ) ,
393+ UniformComponentPlugin :: < T > :: default ( ) ,
394+ ) ) ;
395+
396+ // We need to get the render app from the main app
397+ let Some ( render_app) = app. get_sub_app_mut ( RenderApp ) else {
398+ return ;
399+ } ;
400+ render_app. add_systems ( RenderStartup , init_pipeline :: < T > ) ;
401+ }
402+ }
403+
404+ pub trait FullscreenMaterial :
405+ Component + ExtractComponent + AsBindGroup + Clone + Copy + ShaderType + WriteInto
406+ {
407+ fn fragment_shader ( ) -> ShaderRef ;
408+ }
409+ #[ derive( Resource ) ]
410+ struct FullscreenMaterialPipeline {
411+ layout : BindGroupLayout ,
412+ sampler : Sampler ,
413+ pipeline_id : CachedRenderPipelineId ,
414+ }
415+ fn init_pipeline < T : FullscreenMaterial > (
416+ mut commands : Commands ,
417+ render_device : Res < RenderDevice > ,
418+ asset_server : Res < AssetServer > ,
419+ fullscreen_shader : Res < FullscreenShader > ,
420+ pipeline_cache : Res < PipelineCache > ,
421+ ) {
422+ // We need to define the bind group layout used for our pipeline
423+ let layout = render_device. create_bind_group_layout (
424+ "post_process_bind_group_layout" ,
425+ & BindGroupLayoutEntries :: sequential (
426+ // The layout entries will only be visible in the fragment stage
427+ ShaderStages :: FRAGMENT ,
428+ (
429+ // The screen texture
430+ texture_2d ( TextureSampleType :: Float { filterable : true } ) ,
431+ // The sampler that will be used to sample the screen texture
432+ sampler ( SamplerBindingType :: Filtering ) ,
433+ // The settings uniform that will control the effect
434+ uniform_buffer :: < T > ( true ) ,
435+ ) ,
436+ ) ,
437+ ) ;
438+ // We can create the sampler here since it won't change at runtime and doesn't depend on the view
439+ let sampler = render_device. create_sampler ( & SamplerDescriptor :: default ( ) ) ;
440+ let mayber_shader = match T :: fragment_shader ( ) {
441+ ShaderRef :: Default => {
442+ unimplemented ! ( "No default fallback for FullscreenMaterial shader" )
443+ }
444+ ShaderRef :: Handle ( handle) => handle,
445+ ShaderRef :: Path ( path) => asset_server. load ( path) ,
446+ } ;
447+ // This will setup a fullscreen triangle for the vertex state.
448+ let vertex_state = fullscreen_shader. to_vertex_state ( ) ;
449+ let pipeline_id = pipeline_cache
450+ // This will add the pipeline to the cache and queue its creation
451+ . queue_render_pipeline ( RenderPipelineDescriptor {
452+ label : Some ( "post_process_pipeline" . into ( ) ) ,
453+ layout : vec ! [ layout. clone( ) ] ,
454+ vertex : vertex_state,
455+ fragment : Some ( FragmentState {
456+ shader,
457+ // Make sure this matches the entry point of your shader.
458+ // It can be anything as long as it matches here and in the shader.
459+ targets : vec ! [ Some ( ColorTargetState {
460+ format: TextureFormat :: bevy_default( ) ,
461+ blend: None ,
462+ write_mask: ColorWrites :: ALL ,
463+ } ) ] ,
464+ ..default ( )
465+ } ) ,
466+ ..default ( )
467+ } ) ;
468+ commands. insert_resource ( FullscreenMaterialPipeline {
469+ layout,
470+ sampler,
471+ pipeline_id,
472+ } ) ;
473+ }
474+ struct FullscreenMaterialNode < T : FullscreenMaterial > {
475+ _marker : PhantomData < T > ,
476+ }
477+
478+ impl < T : FullscreenMaterial > ViewNode for FullscreenMaterialNode < T > {
479+ // The node needs a query to gather data from the ECS in order to do its rendering,
480+ // but it's not a normal system so we need to define it manually.
481+ //
482+ // This query will only run on the view entity
483+ type ViewQuery = (
484+ & ' static ViewTarget ,
485+ // This makes sure the node only runs on cameras with the PostProcessSettings component
486+ & ' static T ,
487+ // As there could be multiple post processing components sent to the GPU (one per camera),
488+ // we need to get the index of the one that is associated with the current view.
489+ & ' static DynamicUniformIndex < T > ,
490+ ) ;
491+
492+ fn run < ' w > (
493+ & self ,
494+ graph : & mut bevy_render:: render_graph:: RenderGraphContext ,
495+ render_context : & mut bevy_render:: renderer:: RenderContext < ' w > ,
496+ view_query : bevy_ecs:: query:: QueryItem < ' w , ' _ , Self :: ViewQuery > ,
497+ world : & ' w World ,
498+ ) -> std:: result:: Result < ( ) , bevy_render:: render_graph:: NodeRunError > {
499+ todo ! ( )
500+ }
501+ }
502+ }
0 commit comments