use bevy::{ prelude::*, reflect::TypeUuid, render::{ mesh::shape, pipeline::{PipelineDescriptor, RenderPipeline}, render_graph::{base, AssetRenderResourcesNode, RenderGraph}, renderer::RenderResources, shader::{ShaderStage, ShaderStages}, }, }; /// This example illustrates how to create a texture for use with a texture2DArray shader uniform variable. fn main() { App::build() .add_plugins(DefaultPlugins) .add_asset::() .add_startup_system(setup.system()) .add_system(create_array_texture.system()) .run(); } #[derive(RenderResources, Default, TypeUuid)] #[uuid = "93fb26fc-6c05-489b-9029-601edf703b6b"] struct MyArrayTexture { pub texture: Handle, } const VERTEX_SHADER: &str = r#" #version 450 layout(location = 0) in vec3 Vertex_Position; layout(location = 0) out vec4 v_Position; layout(set = 0, binding = 0) uniform Camera { mat4 ViewProj; }; layout(set = 1, binding = 0) uniform Transform { mat4 Model; }; void main() { v_Position = ViewProj * Model * vec4(Vertex_Position, 1.0); gl_Position = v_Position; } "#; const FRAGMENT_SHADER: &str = r#" #version 450 layout(location = 0) in vec4 v_Position; layout(location = 0) out vec4 o_Target; layout(set = 2, binding = 0) uniform texture2DArray MyArrayTexture_texture; layout(set = 2, binding = 1) uniform sampler MyArrayTexture_texture_sampler; void main() { // Screen-space coordinates determine which layer of the array texture we sample. vec2 ss = v_Position.xy / v_Position.w; float layer = 0.0; if (ss.x > 0.0 && ss.y > 0.0) { layer = 0.0; } else if (ss.x < 0.0 && ss.y > 0.0) { layer = 1.0; } else if (ss.x > 0.0 && ss.y < 0.0) { layer = 2.0; } else { layer = 3.0; } // Convert to texture coordinates. vec2 uv = (ss + vec2(1.0)) / 2.0; o_Target = texture(sampler2DArray(MyArrayTexture_texture, MyArrayTexture_texture_sampler), vec3(uv, layer)); } "#; struct LoadingTexture(Option>); struct MyPipeline(Handle); fn setup( commands: &mut Commands, asset_server: Res, mut pipelines: ResMut>, mut shaders: ResMut>, mut render_graph: ResMut, ) { // Start loading the texture. commands.insert_resource(LoadingTexture(Some( asset_server.load("textures/array_texture.png"), ))); // Create a new shader pipeline. let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages { vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)), fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))), })); commands.insert_resource(MyPipeline(pipeline_handle)); // Add an AssetRenderResourcesNode to our Render Graph. This will bind MyArrayTexture resources to our shader. render_graph.add_system_node( "my_array_texture", AssetRenderResourcesNode::::new(true), ); // Add a Render Graph edge connecting our new "my_array_texture" node to the main pass node. This ensures "my_array_texture" // runs before the main pass. render_graph .add_node_edge("my_array_texture", base::node::MAIN_PASS) .unwrap(); commands.spawn(Camera3dBundle { transform: Transform::from_translation(Vec3::new(2.0, 2.0, 2.0)) .looking_at(Vec3::default(), Vec3::unit_y()), ..Default::default() }); } fn create_array_texture( commands: &mut Commands, my_pipeline: Res, mut loading_texture: ResMut, mut textures: ResMut>, mut meshes: ResMut>, mut array_textures: ResMut>, ) { let (handle, texture) = match loading_texture.0.as_ref() { Some(handle) => { if let Some(texture) = textures.get_mut(handle) { (loading_texture.0.take().unwrap(), texture) } else { return; } } None => return, }; // Create a new array texture asset from the loaded texture. let array_layers = 4; texture.reinterpret_stacked_2d_as_array(array_layers); let array_texture = array_textures.add(MyArrayTexture { texture: handle }); // Spawn a cube that's shaded using the array texture. commands .spawn(MeshBundle { mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new( my_pipeline.0.clone(), )]), ..Default::default() }) .with(array_texture); }