-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Add support for flat shading with indiced meshes #3008
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,19 @@ | ||
#version 450 | ||
|
||
layout(location = 0) in vec3 Vertex_Position; | ||
#ifndef STANDARDMATERIAL_FLAT_SHADING | ||
layout(location = 1) in vec3 Vertex_Normal; | ||
#endif | ||
layout(location = 2) in vec2 Vertex_Uv; | ||
|
||
#ifdef STANDARDMATERIAL_NORMAL_MAP | ||
layout(location = 3) in vec4 Vertex_Tangent; | ||
#endif | ||
|
||
layout(location = 0) out vec3 v_WorldPosition; | ||
#ifndef STANDARDMATERIAL_FLAT_SHADING | ||
layout(location = 1) out vec3 v_WorldNormal; | ||
#endif | ||
layout(location = 2) out vec2 v_Uv; | ||
|
||
layout(set = 0, binding = 0) uniform CameraViewProj { | ||
|
@@ -27,7 +31,9 @@ layout(set = 2, binding = 0) uniform Transform { | |
void main() { | ||
vec4 world_position = Model * vec4(Vertex_Position, 1.0); | ||
v_WorldPosition = world_position.xyz; | ||
#ifndef STANDARDMATERIAL_FLAT_SHADING | ||
v_WorldNormal = mat3(Model) * Vertex_Normal; | ||
#endif | ||
v_Uv = Vertex_Uv; | ||
#ifdef STANDARDMATERIAL_NORMAL_MAP | ||
v_WorldTangent = vec4(mat3(Model) * Vertex_Tangent.xyz, Vertex_Tangent.w); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you given any thought as to how this interacts with tangents? (Just curious as though I don't know how I'd approach this to be honest). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not, I assumed since the tangent uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, thinking this through a little harder, the tangent is used in tandem with the normal to create a bitangent. After that, all three are used to create a matrix to transform the sampled normal from the map. As a user I'd expect one of two things to happen:
While I would probably feel like the first option would be what I go with, it seems that the second one is more logical. |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
use bevy::prelude::*; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the top of this example, I'd love to see a quick write-up of what flat vs smooth shading means, and why you might prefer one over the other. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I've improved the commenting. |
||
|
||
/// This example shows the difference between flat shading | ||
/// and smooth shading (default) in `StandardMaterial`. | ||
/// Flat shading gives a much more "Polygonal" or "Retro" look to meshes. | ||
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>>, | ||
) { | ||
// Flat shaded icosphere (ORANGE) | ||
commands.spawn_bundle(PbrBundle { | ||
mesh: meshes.add(Mesh::from(shape::Icosphere { | ||
radius: 0.5, | ||
subdivisions: 4, | ||
})), | ||
material: materials.add(StandardMaterial { | ||
base_color: Color::ORANGE, | ||
flat_shading: true, | ||
..Default::default() | ||
}), | ||
transform: Transform::from_xyz(-0.55, 0.5, 0.0), | ||
..Default::default() | ||
}); | ||
// Smooth shaded icosphere (BLUE) | ||
commands.spawn_bundle(PbrBundle { | ||
mesh: meshes.add(Mesh::from(shape::Icosphere { | ||
radius: 0.5, | ||
subdivisions: 4, | ||
})), | ||
material: materials.add(Color::BLUE.into()), | ||
transform: Transform::from_xyz(0.55, 0.5, 0.0), | ||
..Default::default() | ||
}); | ||
|
||
// 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::default() | ||
}); | ||
// light | ||
commands.spawn_bundle(PointLightBundle { | ||
transform: Transform::from_xyz(5.0, 5.0, 5.0), | ||
..Default::default() | ||
}); | ||
// camera | ||
commands.spawn_bundle(PerspectiveCameraBundle { | ||
transform: Transform::from_xyz(1.0, 3.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y), | ||
..Default::default() | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the derivative solution to making flat shading can cause artifacts should the edge of the rasterized mesh not contain the entire group (IIRC, dfdx and dfdy are computed using the neighbouring pixels' values, given that fragments are calculated in 2x2 groups), I'd probably make it clear that this is not foolproof and might not work in all situations.
Some other solutions which are more complex:
flat
interpolators, however that's only possible in certain special cases of geometry (Only possible when you can assign one vertex to each triangle).primitive_id
or equivalent into a buffer containing precalculated normal values. This is a bit of a pain because it takes a while to set up, however it lets you save on space because then you don't need to store values per-vertex. Although, another thing to note is that it's not incredibly portable since it uses the same hardware as geometry shaders (although it itself is not a geometry shader based approach).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a better docstring to the field saying it can cause artifacts, I also added a docstring to the field saying its possible to do the last option by calling
Mesh::duplicate_vertices()
andMesh::compute_flat_normals()
while not setting this field true. Would that be acceptable?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that the new docstring is good; the other methods I described should really be up to the user.
🤔 I really should write up a blog post somewhere about the various methods to achieve flat shading...