Skip to content

Commit 6ce57c8

Browse files
akirosscart
andcommitted
Example on how to draw using custom mesh and shader (#1565)
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>
1 parent 9186c4c commit 6ce57c8

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ path = "examples/hello_world.rs"
9797
name = "contributors"
9898
path = "examples/2d/contributors.rs"
9999

100+
[[example]]
101+
name = "mesh"
102+
path = "examples/2d/mesh.rs"
103+
100104
[[example]]
101105
name = "many_sprites"
102106
path = "examples/2d/many_sprites.rs"

examples/2d/mesh.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use bevy::{
2+
prelude::*,
3+
render::{
4+
pipeline::{PipelineDescriptor, RenderPipeline},
5+
shader::{ShaderStage, ShaderStages},
6+
},
7+
};
8+
9+
fn main() {
10+
App::build()
11+
.add_plugins(DefaultPlugins)
12+
.add_startup_system(star.system())
13+
.run();
14+
}
15+
16+
fn star(
17+
mut commands: Commands,
18+
// We will add a new Mesh for the star being created
19+
mut meshes: ResMut<Assets<Mesh>>,
20+
// A pipeline will be added with custom shaders
21+
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
22+
// Access to add new shaders
23+
mut shaders: ResMut<Assets<Shader>>,
24+
) {
25+
// We first create a pipeline, which is the sequence of steps that are
26+
// needed to get to pixels on the screen starting from a description of the
27+
// geometries in the scene. Pipelines have fixed steps, which sometimes can
28+
// be turned off (for instance, depth and stencil tests) and programmable
29+
// steps, the vertex and fragment shaders, that we can customize writing
30+
// shader programs.
31+
32+
let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages {
33+
// Vertex shaders are run once for every vertex in the mesh.
34+
// Each vertex can have attributes associated to it (e.g. position,
35+
// color, texture mapping). The output of a shader is per-vertex.
36+
vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)),
37+
// Fragment shaders are run for each pixel belonging to a triangle on
38+
// the screen. Their output is per-pixel.
39+
fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
40+
}));
41+
42+
// Let's define the mesh for the object we want to draw: a nice star.
43+
// We will specify here what kind of topology is used to define the mesh,
44+
// that is, how triangles are built from the vertices. We will use a
45+
// triangle list, meaning that each vertex of the triangle has to be
46+
// specified.
47+
let mut star = Mesh::new(bevy::render::pipeline::PrimitiveTopology::TriangleList);
48+
49+
// Vertices need to have a position attribute. We will use the following
50+
// vertices (I hope you can spot the star in the schema).
51+
//
52+
// 1
53+
//
54+
// 10 2
55+
// 9 0 3
56+
// 8 4
57+
// 6
58+
// 7 5
59+
//
60+
// These vertices are specificed in 3D space.
61+
let mut v_pos = vec![[0.0, 0.0, 0.0]];
62+
for i in 0..10 {
63+
// Angle of each vertex is 1/10 of TAU, plus PI/2 for positioning vertex 0
64+
let a = std::f32::consts::FRAC_PI_2 - i as f32 * std::f32::consts::TAU / 10.0;
65+
// Radius of internal vertices (2, 4, 6, 8, 10) is 100, it's 200 for external
66+
let r = (1 - i % 2) as f32 * 100.0 + 100.0;
67+
// Add the vertex coordinates
68+
v_pos.push([r * a.cos(), r * a.sin(), 0.0]);
69+
}
70+
// Set the position attribute
71+
star.set_attribute(Mesh::ATTRIBUTE_POSITION, v_pos);
72+
// And a RGB color attribute as well
73+
let mut v_color = vec![[0.0, 0.0, 0.0]];
74+
v_color.extend_from_slice(&[[1.0, 1.0, 0.0]; 10]);
75+
star.set_attribute("Vertex_Color", v_color);
76+
77+
// Now, we specify the indices of the vertex that are going to compose the
78+
// triangles in our star. Vertices in triangles have to be specified in CCW
79+
// winding (that will be the front face, colored). Since we are using
80+
// triangle list, we will specify each triangle as 3 vertices
81+
// First triangle: 0, 2, 1
82+
// Second triangle: 0, 3, 2
83+
// Third triangle: 0, 4, 3
84+
// etc
85+
// Last triangle: 0, 1, 10
86+
let mut indices = vec![0, 1, 10];
87+
for i in 2..=10 {
88+
indices.extend_from_slice(&[0, i, i - 1]);
89+
}
90+
star.set_indices(Some(bevy::render::mesh::Indices::U32(indices)));
91+
92+
// We can now spawn the entities for the star and the camera
93+
commands.spawn_bundle(MeshBundle {
94+
mesh: meshes.add(star),
95+
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(
96+
pipeline_handle,
97+
)]),
98+
..Default::default()
99+
});
100+
commands
101+
// And use an orthographic projection
102+
.spawn_bundle(OrthographicCameraBundle::new_2d());
103+
}
104+
105+
const VERTEX_SHADER: &str = r"
106+
#version 450
107+
108+
layout(location = 0) in vec3 Vertex_Position;
109+
layout(location = 1) in vec3 Vertex_Color;
110+
111+
layout(location = 1) out vec3 v_Color;
112+
113+
layout(set = 0, binding = 0) uniform CameraViewProj {
114+
mat4 ViewProj;
115+
};
116+
117+
layout(set = 1, binding = 0) uniform Transform {
118+
mat4 Model;
119+
};
120+
121+
void main() {
122+
v_Color = Vertex_Color;
123+
gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
124+
}
125+
";
126+
127+
const FRAGMENT_SHADER: &str = r"
128+
#version 450
129+
130+
layout(location = 1) in vec3 v_Color;
131+
132+
layout(location = 0) out vec4 o_Target;
133+
134+
void main() {
135+
o_Target = vec4(v_Color, 1.0);
136+
}
137+
";

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Example | Main | Description
7373
--- | --- | ---
7474
`contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball!
7575
`many_sprites` | [`2d/many_sprites.rs`](./2d/many_sprites.rs) | Displays many sprites in a grid arragement! Used for performance testing.
76+
`mesh` | [`2d/mesh.rs`](./2d/mesh.rs) | Renders a custom mesh
7677
`sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite
7778
`sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite
7879
`text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d

0 commit comments

Comments
 (0)