Skip to content

bevy_pbr2: added vertex color support #3187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ path = "examples/3d/pbr.rs"
name = "pbr_pipelined"
path = "examples/3d/pbr_pipelined.rs"

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

[[example]]
name = "render_to_texture"
path = "examples/3d/render_to_texture.rs"
Expand Down
83 changes: 83 additions & 0 deletions examples/3d/pbr_colors_pipelined.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use bevy::{
math::Vec3,
pbr2::{PbrBundle, PointLightBundle, StandardMaterial},
prelude::{App, Assets, Commands, ResMut, Transform},
render2::{
camera::PerspectiveCameraBundle,
mesh::{shape, Mesh, VertexAttributeValues},
},
PipelinedDefaultPlugins,
};

/// This example illustrates how to use the vertex colors attribute.
fn main() {
App::new()
.add_plugins(PipelinedDefaultPlugins)
.add_startup_system(setup)
.run();
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// create a generic cube
let mut cube_with_colors = Mesh::from(shape::Cube { size: 2.0 });

// set some nice nice colors!
cube_with_colors.set_attribute(
Mesh::ATTRIBUTE_COLOR,
// NOTE: the attribute count has to be consistent across all attributes, otherwise bevy
// will panic.
VertexAttributeValues::from(vec![
// top
[0.79, 0.73, 0.07, 1.],
[0.74, 0.14, 0.29, 1.],
[0.08, 0.55, 0.74, 1.],
[0.20, 0.27, 0.29, 1.],
// bottom
[0.79, 0.73, 0.07, 1.],
[0.74, 0.14, 0.29, 1.],
[0.08, 0.55, 0.74, 1.],
[0.20, 0.27, 0.29, 1.],
// right
[0.79, 0.73, 0.07, 1.],
[0.74, 0.14, 0.29, 1.],
[0.08, 0.55, 0.74, 1.],
[0.20, 0.27, 0.29, 1.],
// left
[0.79, 0.73, 0.07, 1.],
[0.74, 0.14, 0.29, 1.],
[0.08, 0.55, 0.74, 1.],
[0.20, 0.27, 0.29, 1.],
// front
[0.79, 0.73, 0.07, 1.],
[0.74, 0.14, 0.29, 1.],
[0.08, 0.55, 0.74, 1.],
[0.20, 0.27, 0.29, 1.],
// back
[0.79, 0.73, 0.07, 1.],
[0.74, 0.14, 0.29, 1.],
[0.08, 0.55, 0.74, 1.],
[0.20, 0.27, 0.29, 1.],
]),
);
// cube
commands.spawn_bundle(PbrBundle {
mesh: meshes.add(cube_with_colors), // use our cube with vertex colors
material: materials.add(Default::default()),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..Default::default()
});
// light
commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default()
});
// camera
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(3.0, 5.0, -8.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Example | File | Description
`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
`wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering
`z_sort_debug` | [`3d/z_sort_debug.rs`](./3d/z_sort_debug.rs) | Visualizes camera Z-ordering
`pbr_colors_pipelined` | [`3d/pbr_colors_pipelined.rs`](./3d/pbr_colors_pipelined.rs) | PBR rendering with custom vertex colors

## Application

Expand Down
116 changes: 60 additions & 56 deletions pipelined/bevy_pbr2/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ bitflags::bitflags! {
const NONE = 0;
const VERTEX_TANGENTS = (1 << 0);
const TRANSPARENT_MAIN_PASS = (1 << 1);
const VERTEX_COLORS = (1 << 2);
const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS;
}
}
Expand All @@ -392,66 +393,69 @@ impl SpecializedPipeline for MeshPipeline {
type Key = MeshPipelineKey;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let (vertex_array_stride, vertex_attributes) =
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
(
48,
vec![
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 40,
shader_location: 2,
},
// Tangent
VertexAttribute {
format: VertexFormat::Float32x4,
offset: 24,
shader_location: 3,
},
],
)
} else {
(
32,
vec![
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
)
};
let mut vertex_attributes: Vec<VertexAttribute> = vec![];
let mut offset = 0;

// Mesh attributes are sorted alphabetically in the buffer, so add them in the correct
// order.

// colors: will be at location 3 or 4 depending on wether Vertex_Tangents are used
if key.contains(MeshPipelineKey::VERTEX_COLORS) {
vertex_attributes.push(VertexAttribute {
format: VertexFormat::Float32x4,
offset,
shader_location: if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
4
} else {
3
},
});
offset += 16;
}

// normals
vertex_attributes.push(VertexAttribute {
format: VertexFormat::Float32x3,
offset,
shader_location: 1,
});
offset += 12;

// positions
vertex_attributes.push(VertexAttribute {
format: VertexFormat::Float32x3,
offset,
shader_location: 0,
});
offset += 12;

// tangents
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
vertex_attributes.push(VertexAttribute {
format: VertexFormat::Float32x4,
offset,
shader_location: 3,
});
offset += 16;
}

// uvs
vertex_attributes.push(VertexAttribute {
format: VertexFormat::Float32x2,
offset,
shader_location: 2,
});
offset += 8;

let vertex_array_stride = offset;

let mut shader_defs = Vec::new();
if key.contains(MeshPipelineKey::VERTEX_TANGENTS) {
shader_defs.push(String::from("VERTEX_TANGENTS"));
}
if key.contains(MeshPipelineKey::VERTEX_COLORS) {
shader_defs.push(String::from("VERTEX_COLORS"));
}

let (label, blend, depth_write_enabled);
if key.contains(MeshPipelineKey::TRANSPARENT_MAIN_PASS) {
Expand Down
27 changes: 26 additions & 1 deletion pipelined/bevy_pbr2/src/render/mesh.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ struct Vertex {
#ifdef VERTEX_TANGENTS
[[location(3)]] tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
#ifdef VERTEX_TANGENTS
[[location(4)]] color: vec4<f32>;
#else
[[location(3)]] color: vec4<f32>;
#endif
#endif
};

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

[[group(2), binding(0)]]
Expand Down Expand Up @@ -45,6 +59,9 @@ fn vertex(vertex: Vertex) -> VertexOutput {
) * vertex.tangent.xyz,
vertex.tangent.w
);
#endif
#ifdef VERTEX_COLORS
out.world_color = vertex.color;
#endif
return out;
}
Expand All @@ -57,9 +74,17 @@ struct FragmentInput {
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
#ifdef VERTEX_TANGENTS
[[location(4)]] world_color: vec4<f32>;
#endif
#ifndef VERTEX_TANGENTS
[[location(3)]] world_color: vec4<f32>;
#endif
#endif
};

[[stage(fragment)]]
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
}
}
3 changes: 3 additions & 0 deletions pipelined/bevy_pbr2/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ pub fn queue_meshes(
if mesh.has_tangents {
pbr_key.mesh_key |= MeshPipelineKey::VERTEX_TANGENTS;
}
if mesh.has_colors {
pbr_key.mesh_key |= MeshPipelineKey::VERTEX_COLORS;
}
}

if let AlphaMode::Blend = material.alpha_mode {
Expand Down
11 changes: 11 additions & 0 deletions pipelined/bevy_pbr2/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,22 @@ struct FragmentInput {
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
#ifdef VERTEX_COLORS
#ifdef VERTEX_TANGENTS
[[location(4)]] world_color: vec4<f32>;
#else
[[location(3)]] world_color: vec4<f32>;
#endif
#endif
};

[[stage(fragment)]]
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
var output_color: vec4<f32> = material.base_color;

#ifdef VERTEX_COLORS
output_color = in.world_color;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't it make more sense for the vertex color to be multiplied with the base color from the material, rather than overriding it?

It seems a lot more useful to me (though I admit I don't know what's the typical/standard behavior for this case / what other game engines do).

That way, the shader combines the color from the texture (if any), the color from the material (if any), and the color from the vertex (if any). This gives the most flexibility, as the user could use all of these variables to create cool graphics effects. It seems strictly more useful than what you are doing right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep totally makes sense. I've actually moved on to another method and personally no longer use the color vertices. I set the material base color to white and then create a custom texture with the colors I need. Instead of using vertex colors I then simply use correct uv coords for the texture. Once that other pr is merged, I'll update this PR and change it to multiply instead. Great suggestion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which other PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think he’s referring to #3120

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought so but didn't see any direct reference. :) I agree it makes sense to get the flexible vertex attributes merged first and then this. I have added both of these to the rendering project board so they get prioritised as these are common problems that people hit.

#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: 2 additions & 0 deletions pipelined/bevy_render2/src/mesh/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ pub struct GpuMesh {
pub vertex_buffer: Buffer,
pub index_info: Option<GpuIndexInfo>,
pub has_tangents: bool,
pub has_colors: bool,
}

/// The index info of a [`GpuMesh`].
Expand Down Expand Up @@ -640,6 +641,7 @@ impl RenderAsset for Mesh {
vertex_buffer,
index_info,
has_tangents: mesh.attributes.contains_key(Mesh::ATTRIBUTE_TANGENT),
has_colors: mesh.attributes.contains_key(Mesh::ATTRIBUTE_COLOR),
})
}
}