bevy/examples/shader/array_texture.rs
2020-12-04 10:37:27 -08:00

153 lines
4.7 KiB
Rust

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::<MyArrayTexture>()
.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<Texture>,
}
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<Handle<Texture>>);
struct MyPipeline(Handle<PipelineDescriptor>);
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
mut shaders: ResMut<Assets<Shader>>,
mut render_graph: ResMut<RenderGraph>,
) {
// 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::<MyArrayTexture>::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<MyPipeline>,
mut loading_texture: ResMut<LoadingTexture>,
mut textures: ResMut<Assets<Texture>>,
mut meshes: ResMut<Assets<Mesh>>,
mut array_textures: ResMut<Assets<MyArrayTexture>>,
) {
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);
}