mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
6ce57c85d6
I was looking into "lower level" rendering and I saw no example on how to do that. Yet, I think it's something relevant to show, so I set up a simple example on how to do that. I hope it's welcome. I'm not confident about the code and a review is definitely nice to have, especially because there are a few things that are not great. Specifically, I think it would be nice to see how to render with a completely custom set of attributes (position and color, in this case), but I couldn't manage to get it working without normals and uv. It makes sense if bevy Meshes need these two attributes, but I'm not sure about it. Co-authored-by: Alessandro Re <ale@ale-re.net> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
137 lines
4.7 KiB
Rust
137 lines
4.7 KiB
Rust
use bevy::{
|
|
prelude::*,
|
|
render::{
|
|
pipeline::{PipelineDescriptor, RenderPipeline},
|
|
shader::{ShaderStage, ShaderStages},
|
|
},
|
|
};
|
|
|
|
fn main() {
|
|
App::build()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_startup_system(star.system())
|
|
.run();
|
|
}
|
|
|
|
fn star(
|
|
mut commands: Commands,
|
|
// We will add a new Mesh for the star being created
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
// A pipeline will be added with custom shaders
|
|
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
|
|
// Access to add new shaders
|
|
mut shaders: ResMut<Assets<Shader>>,
|
|
) {
|
|
// We first create a pipeline, which is the sequence of steps that are
|
|
// needed to get to pixels on the screen starting from a description of the
|
|
// geometries in the scene. Pipelines have fixed steps, which sometimes can
|
|
// be turned off (for instance, depth and stencil tests) and programmable
|
|
// steps, the vertex and fragment shaders, that we can customize writing
|
|
// shader programs.
|
|
|
|
let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages {
|
|
// Vertex shaders are run once for every vertex in the mesh.
|
|
// Each vertex can have attributes associated to it (e.g. position,
|
|
// color, texture mapping). The output of a shader is per-vertex.
|
|
vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)),
|
|
// Fragment shaders are run for each pixel belonging to a triangle on
|
|
// the screen. Their output is per-pixel.
|
|
fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
|
|
}));
|
|
|
|
// Let's define the mesh for the object we want to draw: a nice star.
|
|
// We will specify here what kind of topology is used to define the mesh,
|
|
// that is, how triangles are built from the vertices. We will use a
|
|
// triangle list, meaning that each vertex of the triangle has to be
|
|
// specified.
|
|
let mut star = Mesh::new(bevy::render::pipeline::PrimitiveTopology::TriangleList);
|
|
|
|
// Vertices need to have a position attribute. We will use the following
|
|
// vertices (I hope you can spot the star in the schema).
|
|
//
|
|
// 1
|
|
//
|
|
// 10 2
|
|
// 9 0 3
|
|
// 8 4
|
|
// 6
|
|
// 7 5
|
|
//
|
|
// These vertices are specificed in 3D space.
|
|
let mut v_pos = vec![[0.0, 0.0, 0.0]];
|
|
for i in 0..10 {
|
|
// Angle of each vertex is 1/10 of TAU, plus PI/2 for positioning vertex 0
|
|
let a = std::f32::consts::FRAC_PI_2 - i as f32 * std::f32::consts::TAU / 10.0;
|
|
// Radius of internal vertices (2, 4, 6, 8, 10) is 100, it's 200 for external
|
|
let r = (1 - i % 2) as f32 * 100.0 + 100.0;
|
|
// Add the vertex coordinates
|
|
v_pos.push([r * a.cos(), r * a.sin(), 0.0]);
|
|
}
|
|
// Set the position attribute
|
|
star.set_attribute(Mesh::ATTRIBUTE_POSITION, v_pos);
|
|
// And a RGB color attribute as well
|
|
let mut v_color = vec![[0.0, 0.0, 0.0]];
|
|
v_color.extend_from_slice(&[[1.0, 1.0, 0.0]; 10]);
|
|
star.set_attribute("Vertex_Color", v_color);
|
|
|
|
// Now, we specify the indices of the vertex that are going to compose the
|
|
// triangles in our star. Vertices in triangles have to be specified in CCW
|
|
// winding (that will be the front face, colored). Since we are using
|
|
// triangle list, we will specify each triangle as 3 vertices
|
|
// First triangle: 0, 2, 1
|
|
// Second triangle: 0, 3, 2
|
|
// Third triangle: 0, 4, 3
|
|
// etc
|
|
// Last triangle: 0, 1, 10
|
|
let mut indices = vec![0, 1, 10];
|
|
for i in 2..=10 {
|
|
indices.extend_from_slice(&[0, i, i - 1]);
|
|
}
|
|
star.set_indices(Some(bevy::render::mesh::Indices::U32(indices)));
|
|
|
|
// We can now spawn the entities for the star and the camera
|
|
commands.spawn_bundle(MeshBundle {
|
|
mesh: meshes.add(star),
|
|
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(
|
|
pipeline_handle,
|
|
)]),
|
|
..Default::default()
|
|
});
|
|
commands
|
|
// And use an orthographic projection
|
|
.spawn_bundle(OrthographicCameraBundle::new_2d());
|
|
}
|
|
|
|
const VERTEX_SHADER: &str = r"
|
|
#version 450
|
|
|
|
layout(location = 0) in vec3 Vertex_Position;
|
|
layout(location = 1) in vec3 Vertex_Color;
|
|
|
|
layout(location = 1) out vec3 v_Color;
|
|
|
|
layout(set = 0, binding = 0) uniform CameraViewProj {
|
|
mat4 ViewProj;
|
|
};
|
|
|
|
layout(set = 1, binding = 0) uniform Transform {
|
|
mat4 Model;
|
|
};
|
|
|
|
void main() {
|
|
v_Color = Vertex_Color;
|
|
gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
|
|
}
|
|
";
|
|
|
|
const FRAGMENT_SHADER: &str = r"
|
|
#version 450
|
|
|
|
layout(location = 1) in vec3 v_Color;
|
|
|
|
layout(location = 0) out vec4 o_Target;
|
|
|
|
void main() {
|
|
o_Target = vec4(v_Color, 1.0);
|
|
}
|
|
";
|