bevy/release-content/migration-guides/mesh_compute_smooth_normals.md
Waridley 9e1e8bc1bc
Add angle-weighted smooth normals implementation (#18383) (#18552)
# Objective

Closes #18383 

## Solution

Given the 2 votes (me and @komadori ) for making angle-weighted normals
the default, I went ahead and did so, moving the face-weighted
implementation to the new `Mesh::compute_face_weighted_normals` method.
I factored out the common code between both into
`Mesh::compute_custom_smooth_normals`, which I went ahead and made
public to make it easier for users to add any other weighting methods
they might come up with.

If any of these decisions are undesirable for any reason, please let me
know and I will gladly make modifications.

## Testing & Showcase

I made a demo that exaggerates the difference at
[Waridley/bevy_smooth_normals_comparison](https://github.com/Waridley/bevy_smooth_normals_comparison).
Screenshots included in the readme.

Another way it could be demonstrated is via scaling a mesh along its
normals, like for generating outline meshes with inverted faces. I'd be
willing to make a demo for that as well.

I also edited and renamed the `compute_smooth_normals` tests to use
face-weighted normals, and added a new test for angle-weighted ones
which validates that all normals of a unit cube have each component
equal to `(±1 / √3) ± f32::EPSILON`.

## Migration Guide

#16050 already did not mention a migration guide, and it is not even in
a stable release yet. If this lands in a 0.16 RC, updating from RC1
would probably not require any changes in the vast majority of cases,
anyway. If someone really needs face-weighted normals, they can switch
to `.compute_face_weighted_normals()` or
`.with_computed_face_weighted_normals()`. And if for some reason they
really liked the old count-weighted implementation from 0.15, there is
an example in the docs for `compute_custom_smooth_normals`.
2025-07-17 19:23:30 +00:00

1.9 KiB

title pull_requests
Smooth normals implementation changed
18552

In Bevy 0.16, Mesh smooth normal calculation used a triangle area-weighted algorithm. In 0.17, the area-weighted algorithm was moved to separate methods, the default implementation was switched to a corner angle-weighted algorithm, and Mesh::compute_custom_smooth_normals was added for other cases.

The angle-weighted method is more suitable for growing or shrinking a mesh along its vertex normals, such as when generating an outline mesh. It also results in more expected lighting behavior for some meshes. In most cases, the difference will be small and no change is needed. However, the new default is somewhat slower, and does not always produce the result desired by an artist. If you preferred the lighting in 0.16, or have a significant performance regression, or needed area-weighted normals for any other reason, you can switch to the new dedicated area-weighted methods.

// Only if the new smooth normals algorithm is unsatisfactory:

let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, default())
    .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
-    .with_computed_smooth_normals();
+    .with_computed_area_weighted_normals;

- mesh.compute_smooth_normals();
+ mesh.compute_area_weighted_normals();

As part of this change, the helper functions face_normal and face_area_normal, were renamed to triangle_normal and triangle_area_normal respectively to better reflect the fact that they do not take an entire geometric face into account.

- use bevy::render::mesh::face_normal;
+ use bevy::render::mesh::triangle_normal;
- let normal = face_normal(a, b, c);
+ let normal = triangle_normal(a, b, c);

- use bevy::render::mesh::face_area_normal;
+ use bevy::render::mesh::triangle_area_normal;
- let normal = face_area_normal(a, b, c);
+ let normal = triangle_area_normal(a, b, c);