Skip to content

Add angle-weighted smooth normals implementation (#18383) #18552

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

Waridley
Copy link
Contributor

@Waridley Waridley commented Mar 25, 2025

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. 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.

@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Mar 25, 2025
@alice-i-cecile alice-i-cecile added this to the 0.17 milestone Mar 25, 2025
@alice-i-cecile
Copy link
Member

This is too late for 0.16, so you should write the migration guide relative to #16050. Thanks for submitting this though: I think this is reasonable.

@komadori if you have the time, a review here would be much appreciated.

Copy link
Contributor

@IceSentry IceSentry left a comment

Choose a reason for hiding this comment

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

I like the general refactor. I haven't checked the math yet but everything looks right to me at first glance.

Could you try to benchmark if the new custom_normals thing doesn't affect performance too much? I'm mildly worried about the overhead of using the callback.

@Waridley
Copy link
Contributor Author

Could you try to benchmark if the new custom_normals thing doesn't affect performance too much? I'm mildly worried about the overhead of using the callback.

Sure, I was kinda curious how significant the regression was from normalizing every triangle normal and computing the angles anyway.

Do you think it would be worth adding an official benchmark in benches, or should I just test it locally and report the results?

@IceSentry
Copy link
Contributor

It doesn't really hurt I guess. I don't expect that many people to touch this code unlike ECS internals, but I see no reasons not to add it.

@Waridley Waridley force-pushed the angle_weighted_normals branch 2 times, most recently from aa4e9f5 to 6244acd Compare March 30, 2025 03:16
@IceSentry
Copy link
Contributor

Could you add the benchmark as a separate PR so we could have a before/after? Your benchmark does compare the new method to the old one, but it doesn't measure the impact of using a callback.

@Waridley
Copy link
Contributor Author

Could you add the benchmark as a separate PR so we could have a before/after? Your benchmark does compare the new method to the old one, but it doesn't measure the impact of using a callback.

Yeah, sorry, I ran out of time to do any more the other day.

FWIW when I tested it locally, there was no difference between marking compute_custom_smooth_normals with #[inline(always)] or #[inline(never)] or manually inlining it.

Interestingly, the difference between angle-weighted and area-weighted was smaller the larger the mesh I tested with, between ~5x for a 64x64 grid to ~3x with a 2048x2048. That's definitely a significant performance difference and I will mention it in the migration guide, but I personally still think it's best to default to angle-weighted since most of the time, people aren't re-computing normals in a tight loop every frame, but rather computing them once on startup or whenever a new terrain chunk loads. Still matters for load times and to avoid brief freezes.

@IceSentry
Copy link
Contributor

Ah, okay, thanks for investigating. I wasn't expecting a big difference, but I just wanted to make sure it wouldn't make it noticeably slower.

As long as the performance is mentioned in the docs comment I'm fine with either being the default.

@Waridley Waridley force-pushed the angle_weighted_normals branch from 6244acd to 870c8e4 Compare March 31, 2025 17:59
github-merge-queue bot pushed a commit that referenced this pull request Mar 31, 2025
# Objective

Benchmark current `compute_*_normals` methods to get a baseline as
requested in
#18552 (comment)

## Solution

Since the change to the default smooth normals method will definitely
cause a regression, but the previous method will remain as an option, I
added two technically-redundant benchmarks but with different names:
`smooth_normals` for whatever default weighting method is used, and
`face_weighted_normals` to benchmark the area-weighted method regardless
of what the default is. Then I'm adding `angle_weighted_normals` in
#18552. I also added `flat_normals` for completeness.
@Waridley Waridley force-pushed the angle_weighted_normals branch from 870c8e4 to 5ed4b76 Compare March 31, 2025 18:58
@Waridley Waridley force-pushed the angle_weighted_normals branch from 5ed4b76 to 470b5bd Compare March 31, 2025 19:00
@Waridley
Copy link
Contributor Author

Migration guide might be a bit wordy, feel free to give feedback.

@Waridley Waridley force-pushed the angle_weighted_normals branch from 470b5bd to 79a6103 Compare March 31, 2025 19:03
@Waridley
Copy link
Contributor Author

I really need to figure out why CI is behaving differently for me locally 😅 I'll have to fix the markdown errors later today.

@Waridley Waridley force-pushed the angle_weighted_normals branch from 79a6103 to a3af249 Compare April 1, 2025 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen D-Straightforward Simple bug fixes and API improvements, docs, test and examples M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Needs-Review Needs reviewer attention (from anyone!) to move forward
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

More geometrically-accurate smooth normals.
3 participants