Skip to content

Commit 4bed8f0

Browse files
HackerFooItsDoot
authored andcommitted
Add support for vertex colors (bevyengine#4528)
# Objective Add support for vertex colors ## Solution This change is modeled after how vertex tangents are handled, so the shader is conditionally compiled with vertex color support if the mesh has the corresponding attribute set. Vertex colors are multiplied by the base color. I'm not sure if this is the best for all cases, but may be useful for modifying vertex colors without creating a new mesh. I chose `VertexFormat::Float32x4`, but I'd prefer 16-bit floats if/when support is added. ## Changelog ### Added - Vertex colors can be specified using the `Mesh::ATTRIBUTE_COLOR` mesh attribute.
1 parent ff560e3 commit 4bed8f0

File tree

10 files changed

+117
-18
lines changed

10 files changed

+117
-18
lines changed

Cargo.toml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ path = "examples/3d/parenting.rs"
196196
name = "pbr"
197197
path = "examples/3d/pbr.rs"
198198

199+
[[example]]
200+
name = "render_to_texture"
201+
path = "examples/3d/render_to_texture.rs"
202+
199203
[[example]]
200204
name = "shadow_biases"
201205
path = "examples/3d/shadow_biases.rs"
@@ -216,10 +220,6 @@ path = "examples/3d/spherical_area_lights.rs"
216220
name = "texture"
217221
path = "examples/3d/texture.rs"
218222

219-
[[example]]
220-
name = "render_to_texture"
221-
path = "examples/3d/render_to_texture.rs"
222-
223223
[[example]]
224224
name = "two_passes"
225225
path = "examples/3d/two_passes.rs"
@@ -228,6 +228,10 @@ path = "examples/3d/two_passes.rs"
228228
name = "update_gltf_scene"
229229
path = "examples/3d/update_gltf_scene.rs"
230230

231+
[[example]]
232+
name = "vertex_colors"
233+
path = "examples/3d/vertex_colors.rs"
234+
231235
[[example]]
232236
name = "wireframe"
233237
path = "examples/3d/wireframe.rs"

crates/bevy_gltf/src/loader.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,12 @@ async fn load_gltf<'a, 'b>(
269269
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
270270
}
271271

272-
// if let Some(vertex_attribute) = reader
273-
// .read_colors(0)
274-
// .map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect()))
275-
// {
276-
// mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
277-
// }
272+
if let Some(vertex_attribute) = reader
273+
.read_colors(0)
274+
.map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect()))
275+
{
276+
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
277+
}
278278

279279
if let Some(iter) = reader.read_joints(0) {
280280
let vertex_attribute = VertexAttributeValues::Uint16x4(iter.into_u16().collect());

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,11 @@ impl SpecializedMeshPipeline for MeshPipeline {
560560
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
561561
}
562562

563+
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
564+
shader_defs.push(String::from("VERTEX_COLORS"));
565+
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
566+
}
567+
563568
// TODO: consider exposing this in shaders in a more generally useful way, such as:
564569
// # if AVAILABLE_STORAGE_BUFFER_BINDINGS == 3
565570
// /* use storage buffers here */
@@ -577,8 +582,8 @@ impl SpecializedMeshPipeline for MeshPipeline {
577582
&& layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
578583
{
579584
shader_defs.push(String::from("SKINNED"));
580-
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(4));
581-
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(5));
585+
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(5));
586+
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(6));
582587
bind_group_layout.push(self.skinned_mesh_layout.clone());
583588
} else {
584589
bind_group_layout.push(self.mesh_layout.clone());

crates/bevy_pbr/src/render/mesh.wgsl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ struct Vertex {
88
#ifdef VERTEX_TANGENTS
99
[[location(3)]] tangent: vec4<f32>;
1010
#endif
11+
#ifdef VERTEX_COLORS
12+
[[location(4)]] color: vec4<f32>;
13+
#endif
1114
#ifdef SKINNED
12-
[[location(4)]] joint_indices: vec4<u32>;
13-
[[location(5)]] joint_weights: vec4<f32>;
15+
[[location(5)]] joint_indices: vec4<u32>;
16+
[[location(6)]] joint_weights: vec4<f32>;
1417
#endif
1518
};
1619

@@ -22,6 +25,9 @@ struct VertexOutput {
2225
#ifdef VERTEX_TANGENTS
2326
[[location(3)]] world_tangent: vec4<f32>;
2427
#endif
28+
#ifdef VERTEX_COLORS
29+
[[location(4)]] color: vec4<f32>;
30+
#endif
2531
};
2632

2733
[[group(2), binding(0)]]
@@ -60,6 +66,9 @@ fn vertex(vertex: Vertex) -> VertexOutput {
6066
);
6167
#endif
6268
#endif
69+
#ifdef VERTEX_COLORS
70+
out.color = vertex.color;
71+
#endif
6372

6473
out.uv = vertex.uv;
6574
out.clip_position = view.view_proj * out.world_position;
@@ -74,9 +83,16 @@ struct FragmentInput {
7483
#ifdef VERTEX_TANGENTS
7584
[[location(3)]] world_tangent: vec4<f32>;
7685
#endif
86+
#ifdef VERTEX_COLORS
87+
[[location(4)]] color: vec4<f32>;
88+
#endif
7789
};
7890

7991
[[stage(fragment)]]
8092
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
93+
#ifdef VERTEX_COLORS
94+
return in.color;
95+
#else
8196
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
82-
}
97+
#endif
98+
}

crates/bevy_pbr/src/render/pbr.wgsl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,11 +465,17 @@ struct FragmentInput {
465465
#ifdef VERTEX_TANGENTS
466466
[[location(3)]] world_tangent: vec4<f32>;
467467
#endif
468+
#ifdef VERTEX_COLORS
469+
[[location(4)]] color: vec4<f32>;
470+
#endif
468471
};
469472

470473
[[stage(fragment)]]
471474
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
472475
var output_color: vec4<f32> = material.base_color;
476+
#ifdef VERTEX_COLORS
477+
output_color = output_color * in.color;
478+
#endif
473479
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
474480
output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv);
475481
}

crates/bevy_render/src/mesh/mesh/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl Mesh {
7474

7575
/// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]
7676
pub const ATTRIBUTE_COLOR: MeshVertexAttribute =
77-
MeshVertexAttribute::new("Vertex_Color", 4, VertexFormat::Uint32);
77+
MeshVertexAttribute::new("Vertex_Color", 4, VertexFormat::Float32x4);
7878

7979
/// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]
8080
pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =

crates/bevy_sprite/src/mesh2d/mesh.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
295295
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
296296
}
297297

298+
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
299+
shader_defs.push(String::from("VERTEX_COLORS"));
300+
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
301+
}
302+
298303
#[cfg(feature = "webgl")]
299304
shader_defs.push(String::from("NO_ARRAY_TEXTURES_SUPPORT"));
300305

examples/2d/mesh2d_manual.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use bevy::{
33
prelude::*,
44
reflect::TypeUuid,
55
render::{
6-
mesh::Indices,
6+
mesh::{Indices, MeshVertexAttribute},
77
render_asset::RenderAssets,
88
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
99
render_resource::{
@@ -72,7 +72,10 @@ fn star(
7272
// And a RGB color attribute as well
7373
let mut v_color: Vec<u32> = vec![Color::BLACK.as_linear_rgba_u32()];
7474
v_color.extend_from_slice(&[Color::YELLOW.as_linear_rgba_u32(); 10]);
75-
star.insert_attribute(Mesh::ATTRIBUTE_COLOR, v_color);
75+
star.insert_attribute(
76+
MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32),
77+
v_color,
78+
);
7679

7780
// Now, we specify the indices of the vertex that are going to compose the
7881
// triangles in our star. Vertices in triangles have to be specified in CCW

examples/3d/vertex_colors.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use bevy::{prelude::*, render::mesh::VertexAttributeValues};
2+
3+
fn main() {
4+
App::new()
5+
.insert_resource(Msaa { samples: 4 })
6+
.add_plugins(DefaultPlugins)
7+
.add_startup_system(setup)
8+
.run();
9+
}
10+
11+
/// set up a simple 3D scene
12+
fn setup(
13+
mut commands: Commands,
14+
mut meshes: ResMut<Assets<Mesh>>,
15+
mut materials: ResMut<Assets<StandardMaterial>>,
16+
) {
17+
// plane
18+
commands.spawn_bundle(PbrBundle {
19+
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
20+
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
21+
..default()
22+
});
23+
// cube
24+
// Assign vertex colors based on vertex positions
25+
let mut colorful_cube = Mesh::from(shape::Cube { size: 1.0 });
26+
if let Some(VertexAttributeValues::Float32x3(positions)) =
27+
colorful_cube.attribute(Mesh::ATTRIBUTE_POSITION)
28+
{
29+
let colors: Vec<[f32; 4]> = positions
30+
.iter()
31+
.map(|[r, g, b]| [(1. - *r) / 2., (1. - *g) / 2., (1. - *b) / 2., 1.])
32+
.collect();
33+
colorful_cube.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
34+
}
35+
commands.spawn_bundle(PbrBundle {
36+
mesh: meshes.add(colorful_cube),
37+
// This is the default color, but note that vertex colors are
38+
// multiplied by the base color, so you'll likely want this to be
39+
// white if using vertex colors.
40+
material: materials.add(Color::rgb(1., 1., 1.).into()),
41+
transform: Transform::from_xyz(0.0, 0.5, 0.0),
42+
..default()
43+
});
44+
// light
45+
commands.spawn_bundle(PointLightBundle {
46+
point_light: PointLight {
47+
intensity: 1500.0,
48+
shadows_enabled: true,
49+
..default()
50+
},
51+
transform: Transform::from_xyz(4.0, 8.0, 4.0),
52+
..default()
53+
});
54+
// camera
55+
commands.spawn_bundle(PerspectiveCameraBundle {
56+
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
57+
..default()
58+
});
59+
}

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ Example | File | Description
115115
`spherical_area_lights` | [`3d/spherical_area_lights.rs`](./3d/spherical_area_lights.rs) | Demonstrates how point light radius values affect light behavior.
116116
`texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials
117117
`update_gltf_scene` | [`3d/update_gltf_scene.rs`](./3d/update_gltf_scene.rs) | Update a scene from a gltf file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene
118+
`vertex_colors` | [`3d/vertex_colors.rs`](./3d/vertex_colors.rs) | Shows the use of vertex colors
118119
`wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering
119120
`3d_shapes` | [`3d/shapes.rs`](./3d/shapes.rs) | A scene showcasing the built-in 3D shapes
120121

0 commit comments

Comments
 (0)