mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
Add support for vertex colors (#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.
This commit is contained in:
parent
f8e0fc190a
commit
82d849d3dc
10 changed files with 117 additions and 18 deletions
12
Cargo.toml
12
Cargo.toml
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 */
|
||||
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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)]]
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
|
@ -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::{
|
||||
|
@ -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
|
||||
|
|
59
examples/3d/vertex_colors.rs
Normal file
59
examples/3d/vertex_colors.rs
Normal file
|
@ -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()
|
||||
});
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue