Skip to content

Commit

Permalink
Add support for vertex colors (#4528)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
HackerFoo committed May 5, 2022
1 parent f8e0fc1 commit 82d849d
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 18 deletions.
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ path = "examples/3d/parenting.rs"
name = "pbr"
path = "examples/3d/pbr.rs"

[[example]]
name = "render_to_texture"
path = "examples/3d/render_to_texture.rs"

[[example]]
name = "shadow_biases"
path = "examples/3d/shadow_biases.rs"
Expand All @@ -216,10 +220,6 @@ path = "examples/3d/spherical_area_lights.rs"
name = "texture"
path = "examples/3d/texture.rs"

[[example]]
name = "render_to_texture"
path = "examples/3d/render_to_texture.rs"

[[example]]
name = "two_passes"
path = "examples/3d/two_passes.rs"
Expand All @@ -228,6 +228,10 @@ path = "examples/3d/two_passes.rs"
name = "update_gltf_scene"
path = "examples/3d/update_gltf_scene.rs"

[[example]]
name = "vertex_colors"
path = "examples/3d/vertex_colors.rs"

[[example]]
name = "wireframe"
path = "examples/3d/wireframe.rs"
Expand Down
12 changes: 6 additions & 6 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,12 @@ async fn load_gltf<'a, 'b>(
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
}

// if let Some(vertex_attribute) = reader
// .read_colors(0)
// .map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect()))
// {
// mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
// }
if let Some(vertex_attribute) = reader
.read_colors(0)
.map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect()))
{
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
}

if let Some(iter) = reader.read_joints(0) {
let vertex_attribute = VertexAttributeValues::Uint16x4(iter.into_u16().collect());
Expand Down
9 changes: 7 additions & 2 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,11 @@ impl SpecializedMeshPipeline for MeshPipeline {
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
}

if layout.contains(Mesh::ATTRIBUTE_COLOR) {
shader_defs.push(String::from("VERTEX_COLORS"));
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
}

// TODO: consider exposing this in shaders in a more generally useful way, such as:
// # if AVAILABLE_STORAGE_BUFFER_BINDINGS == 3
// /* use storage buffers here */
Expand All @@ -577,8 +582,8 @@ impl SpecializedMeshPipeline for MeshPipeline {
&& layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
{
shader_defs.push(String::from("SKINNED"));
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(4));
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(5));
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(5));
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(6));
bind_group_layout.push(self.skinned_mesh_layout.clone());
} else {
bind_group_layout.push(self.mesh_layout.clone());
Expand Down
22 changes: 19 additions & 3 deletions crates/bevy_pbr/src/render/mesh.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ struct Vertex {
#ifdef VERTEX_TANGENTS
[[location(3)]] tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
[[location(4)]] color: vec4<f32>;
#endif
#ifdef SKINNED
[[location(4)]] joint_indices: vec4<u32>;
[[location(5)]] joint_weights: vec4<f32>;
[[location(5)]] joint_indices: vec4<u32>;
[[location(6)]] joint_weights: vec4<f32>;
#endif
};

Expand All @@ -22,6 +25,9 @@ struct VertexOutput {
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
[[location(4)]] color: vec4<f32>;
#endif
};

[[group(2), binding(0)]]
Expand Down Expand Up @@ -60,6 +66,9 @@ fn vertex(vertex: Vertex) -> VertexOutput {
);
#endif
#endif
#ifdef VERTEX_COLORS
out.color = vertex.color;
#endif

out.uv = vertex.uv;
out.clip_position = view.view_proj * out.world_position;
Expand All @@ -74,9 +83,16 @@ struct FragmentInput {
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
[[location(4)]] color: vec4<f32>;
#endif
};

[[stage(fragment)]]
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
#ifdef VERTEX_COLORS
return in.color;
#else
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
}
#endif
}
6 changes: 6 additions & 0 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -465,11 +465,17 @@ struct FragmentInput {
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
[[location(4)]] color: vec4<f32>;
#endif
};

[[stage(fragment)]]
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
var output_color: vec4<f32> = material.base_color;
#ifdef VERTEX_COLORS
output_color = output_color * in.color;
#endif
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/mesh/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Mesh {

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

/// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]
pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_sprite/src/mesh2d/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
}

if layout.contains(Mesh::ATTRIBUTE_COLOR) {
shader_defs.push(String::from("VERTEX_COLORS"));
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
}

#[cfg(feature = "webgl")]
shader_defs.push(String::from("NO_ARRAY_TEXTURES_SUPPORT"));

Expand Down
7 changes: 5 additions & 2 deletions examples/2d/mesh2d_manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bevy::{
prelude::*,
reflect::TypeUuid,
render::{
mesh::Indices,
mesh::{Indices, MeshVertexAttribute},
render_asset::RenderAssets,
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
render_resource::{
Expand Down Expand Up @@ -72,7 +72,10 @@ fn star(
// And a RGB color attribute as well
let mut v_color: Vec<u32> = vec![Color::BLACK.as_linear_rgba_u32()];
v_color.extend_from_slice(&[Color::YELLOW.as_linear_rgba_u32(); 10]);
star.insert_attribute(Mesh::ATTRIBUTE_COLOR, v_color);
star.insert_attribute(
MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32),
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
Expand Down
59 changes: 59 additions & 0 deletions examples/3d/vertex_colors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use bevy::{prelude::*, render::mesh::VertexAttributeValues};

fn main() {
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.run();
}

/// set up a simple 3D scene
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// plane
commands.spawn_bundle(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default()
});
// cube
// Assign vertex colors based on vertex positions
let mut colorful_cube = Mesh::from(shape::Cube { size: 1.0 });
if let Some(VertexAttributeValues::Float32x3(positions)) =
colorful_cube.attribute(Mesh::ATTRIBUTE_POSITION)
{
let colors: Vec<[f32; 4]> = positions
.iter()
.map(|[r, g, b]| [(1. - *r) / 2., (1. - *g) / 2., (1. - *b) / 2., 1.])
.collect();
colorful_cube.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
}
commands.spawn_bundle(PbrBundle {
mesh: meshes.add(colorful_cube),
// This is the default color, but note that vertex colors are
// multiplied by the base color, so you'll likely want this to be
// white if using vertex colors.
material: materials.add(Color::rgb(1., 1., 1.).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
});
// light
commands.spawn_bundle(PointLightBundle {
point_light: PointLight {
intensity: 1500.0,
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});
// camera
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Example | File | Description
`spherical_area_lights` | [`3d/spherical_area_lights.rs`](./3d/spherical_area_lights.rs) | Demonstrates how point light radius values affect light behavior.
`texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials
`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
`vertex_colors` | [`3d/vertex_colors.rs`](./3d/vertex_colors.rs) | Shows the use of vertex colors
`wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering
`3d_shapes` | [`3d/shapes.rs`](./3d/shapes.rs) | A scene showcasing the built-in 3D shapes

Expand Down

0 comments on commit 82d849d

Please sign in to comment.