Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add animate shaders example #1765

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ name = "scene"
path = "examples/scene/scene.rs"

# Shaders
[[example]]
name = "animate_shader"
path = "examples/shader/animate_shader.rs"

[[example]]
name = "array_texture"
path = "examples/shader/array_texture.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ Example | File | Description

Example | File | Description
--- | --- | ---
`animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable
`array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable
`hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running
`mesh_custom_attribute` | [`shader/mesh_custom_attribute.rs`](./shader/mesh_custom_attribute.rs) | Illustrates how to add a custom attribute to a mesh and use it in a custom shader
Expand Down
124 changes: 124 additions & 0 deletions examples/shader/animate_shader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use bevy::{
prelude::*,
reflect::TypeUuid,
render::{
mesh::shape,
pipeline::{PipelineDescriptor, RenderPipeline},
render_graph::{base, RenderGraph, RenderResourcesNode},
renderer::RenderResources,
shader::{ShaderStage, ShaderStages},
},
};

/// This example shows how to animate a shader, by passing the global `time.seconds_since_startup()`
/// via a 'TimeComponent` to the shader.
pub fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(animate_shader.system())
.run();
}

#[derive(RenderResources, Default, TypeUuid)]
#[uuid = "463e4b8a-d555-4fc2-ba9f-4c880063ba92"]
struct TimeUniform {
value: f32,
}

const VERTEX_SHADER: &str = r#"
#version 450

layout(location = 0) in vec3 Vertex_Position;
layout(location = 1) in vec2 Vertex_Uv;
layout(location = 0) out vec2 v_Uv;

layout(set = 0, binding = 0) uniform CameraViewProj {
mat4 ViewProj;
};

layout(set = 1, binding = 0) uniform Transform {
mat4 Model;
};

void main() {
gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
v_Uv = Vertex_Uv;
}
"#;

const FRAGMENT_SHADER: &str = r#"
#version 450

layout(location = 0) in vec2 v_Uv;
layout(location = 0) out vec4 o_Target;

layout(set = 2, binding = 0) uniform TimeUniform_value {
float time;
};

void main() {
float speed = 0.7;
float translation = sin(time * speed);
float percentage = 0.6;
float threshold = v_Uv.x + translation * percentage;

vec3 red = vec3(1., 0., 0.);
vec3 blue = vec3(0., 0., 1.);
vec3 mixed = mix(red, blue, threshold);

o_Target = vec4(mixed, 1.0);
}
"#;

fn setup(
mut commands: Commands,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
mut shaders: ResMut<Assets<Shader>>,
mut meshes: ResMut<Assets<Mesh>>,
mut render_graph: ResMut<RenderGraph>,
) {
// 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))),
}));

// Add a `RenderResourcesNode` to our `RenderGraph`. This will bind `TimeComponent` to our shader.
render_graph.add_system_node(
"time_uniform",
RenderResourcesNode::<TimeUniform>::new(true),
);

// Add a `RenderGraph` edge connecting our new "time_component" node to the main pass node. This
// ensures that "time_component" runs before the main pass.
render_graph
.add_node_edge("time_uniform", base::node::MAIN_PASS)
.unwrap();

// Spawn a quad and insert the `TimeComponent`.
commands
.spawn_bundle(MeshBundle {
mesh: meshes.add(Mesh::from(shape::Quad::new(Vec2::new(5.0, 5.0)))),
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(
pipeline_handle,
)]),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..Default::default()
})
.insert(TimeUniform { value: 0.0 });

// Spawn a camera.
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
}

/// In this system we query for the `TimeComponent` and global `Time` resource, and set `time.seconds_since_startup()`
/// as the `value` of the `TimeComponent`. This value will be accessed by the fragment shader and used
/// to animate the shader.
fn animate_shader(time: Res<Time>, mut query: Query<&mut TimeUniform>) {
let mut time_uniform = query.single_mut().unwrap();
time_uniform.value = time.seconds_since_startup() as f32;
}