Skip to content

More geometrically-accurate smooth normals. #18383

@Waridley

Description

@Waridley

Problem

#16050 added a much-needed fix to computed smooth normals, where the previous implementation (still in 0.15) resulted in normals weighted by the count of triangles in each plane connected to a vertex. The current implementation instead weights them by the area of each triangle, which is sometimes entirely desired.

However, in a lot of cases, the area of a triangle connected to a vertex has nothing to do with the area of the entire face on that side, like it would in a full 3D modelling program like Blender. And even if it were the same circumstance as Blender, non-face-weighted normals seems to be the default in most cases anyway. For example, the normal of the maximum corner of a mesh generated from an AABB should always be equal to Vec3::ONE.normalize(), no matter the dimensions of the box or the direction the quads are divided into triangles. (In one direction, a face would contribute half of its area, and in the other, it would contribute the full area).

Solution

IMHO, the normals should by default be weighted by the angle of the corner of the triangle at that vertex, as explained in this StackOverflow answer. I have made a demo comparing the current implementation with one that uses corner angles rather than face area: https://github.com/Waridley/bevy_smooth_normals_comparison?tab=readme-ov-file

Open questions

Should it actually be the default?

main branch impl on the left, angle-weighted on the right

This view does highlight another potential drawback of this approach: The normal in the concave corner where the gable meets the spire is almost entirely influenced by the spire face and not the ridge of the gable. It's unclear that that is strictly incorrect, but since the far left vertex of the ridge of the gable is pointing significantly more in the upward direction, scaling the mesh along the normals would result in a more tilted ridge. It also results in a more pronounced faux-ambient occlusion effect. I still think the perfectly-vertical spire peak normal is by far the best option, though.

Naming

If the angle-weighted method does not become the default, but does get added as another method, that method would need a name. I've been calling them "geometric" normals, but I'm not sure how clear that is.

If the area-weighted version remains an option, is it really clear that only the triangles connected to the vertex are incorporated, not the whole plane on that side of the object? Maybe its name could stay but the docs could be updated to clarify that.

Additional context

I discovered the issue with 0.15's smooth normals when I tried to generate outline meshes and they were very lumpy. It turns out bevy_mod_outline also had to deal with that issue and ended up adding an entire separate mesh attribute for it. They might want to keep it around for other reasons anyway, but it seems to me like generating "geometric" smooth normals by default would be less surprising.

I can go ahead and make the PR to update Bevy's implementation, either going with my gut on the unanswered questions and updating later if necessary, or including any feedback provided before I actually make the PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-glTFRelated to the glTF 3D scene/model formatC-FeatureA new feature, making something new possibleD-ModestA "normal" level of difficulty; suitable for simple features or challenging fixesS-Needs-DesignThis issue requires design work to think about how it would best be accomplished

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions