zeux / meshoptimizer

Mesh optimization library that makes meshes smaller and faster to render
MIT License
5.49k stars 473 forks source link

Simplify error calculation question / issue #661

Closed marwie closed 5 months ago

marwie commented 5 months ago

Hi,

I'm currently using gltf-transform to simplify meshes and as discussed in this issue my settings are ratio: 0, error: 0.15

This does however result in extreme decimation for some models where it seems that much more vertices are removed than expected resulting in an almost broken model.

According to the log we get an error of 12 % but the result doesnt look like 12 %

[needle_mesh_transform]: Welding primitive of mesh "Cube" → tolerance: 0.0001
weld: Tolerance thresholds: POSITION=0.019145433044433596, NORMAL=0.05, TANGENT=0.05, TEXCOORD_0=0.0001, TEXCOORD_1=0.0001
weld: 4,820 → 4,820 (+0.00%) vertices.
[needle_mesh_transform]: Simplifying primitive of mesh "Cube" → ratio: 0, error: 0.15, lockBorder: false
simplify: 4,820 → 84 (–98.26%) vertices, error: 0.1239.

Attached is a ZIP with both source files as well as the decimated meshes

mesh simplify.zip

source mesh image

simplified mesh with ratio 0 error 0.15 image

marwie commented 5 months ago

Some more tests

simplification with an ratio of 0.5 results in a reduction of 90 %

[needle_mesh_transform]: Simplifying primitive of mesh "Cube" → ratio: 0.5, error: 0.15, lockBorder: false
simplify: 4,820 → 474 (–90.17%) vertices, error: 0.0066.
[needle_mesh_transform]: Simplifying primitive of mesh "Cube" → ratio: 0.2, error: 0.15, lockBorder: false
simplify: 4,820 → 214 (–95.56%) vertices, error: 0.0165.

other mesh with ratio 0.5 and 0.25 and error 1

NEEDLE_progressive: Min.glb LOD 0
[needle_mesh_transform]: Welding primitive of mesh "" → tolerance: 0.0001
weld: Tolerance thresholds: POSITION=0.0004904861450195313, NORMAL=0.05, TANGENT=0.05, TEXCOORD_0=0.0001
weld: 65,532 → 65,532 (+0.00%) vertices.
[needle_mesh_transform]: Simplifying primitive of mesh "" → ratio: 0, error: 0, lockBorder: false
simplify: 65,532 → 64,863 (–1.02%) vertices, error: 0.0000.
NEEDLE_progressive: Write mesh LOD 0 to "mesh_lod_0_16337220340951744_b4e5b35069a359845847b51b084082c2.glb" with: {"error":0}
NEEDLE_progressive: Min.glb LOD 1
[needle_mesh_transform]: Welding primitive of mesh "" → tolerance: 0.0001
weld: Tolerance thresholds: POSITION=0.0004904861450195313, NORMAL=0.05, TANGENT=0.05, TEXCOORD_0=0.0001
weld: 65,532 → 65,532 (+0.00%) vertices.
[needle_mesh_transform]: Simplifying primitive of mesh "" → ratio: 0.5, error: 1, lockBorder: false
simplify: 65,532 → 11,351 (–82.68%) vertices, error: 0.0003.
NEEDLE_progressive: Write mesh LOD 1 to "mesh_lod_1_16337220340951744_b4e5b35069a359845847b51b084082c2.glb" with: {"ratio":0.5,"error":1}
NEEDLE_progressive: Min.glb LOD 2
[needle_mesh_transform]: Welding primitive of mesh "Object_2" → tolerance: 0.0001
weld: Tolerance thresholds: POSITION=0.0004904861450195313, NORMAL=0.05, TANGENT=0.05, TEXCOORD_0=0.0001
weld: 65,532 → 65,532 (+0.00%) vertices.
[needle_mesh_transform]: Simplifying primitive of mesh "Object_2" → ratio: 0.25, error: 1, lockBorder: false
simplify: 65,532 → 9,775 (–85.08%) vertices, error: 0.0013.
zeux commented 5 months ago

simplification with an ratio of 0.5 results in a reduction of 90 %

That is a gltf-transform issue; probably fixed by https://github.com/donmccurdy/glTF-Transform/pull/1268?

zeux commented 5 months ago

Attaching screenshots from the input sample, simplified using gltfpack with a local patch that allows to set target to 0 and target error to 0.15:

Screenshot from 2024-03-13 09-37-57 Screenshot from 2024-03-13 09-38-00

The input has 7 meshes and internal simplification stats say the error reached in this case is 0.145 (14.5%) - I'd double check if gltf-transform maybe returns an average error instead of the maximum.

The error 15% means that the resulting mesh can be within 0.15 * mesh diameter from the original mesh - any vertex is allowed to move this far (it's a little more complicated than vertex movement distance, it's trying to approximate Hausdorff distance without computing it exactly). Broadly on a screenshot we can approximate this as "silhouette of the resulting mesh is within 15% of the image size of the original mesh".

I've overlayed two screenshots here (roughly aligning based on eye positions), with a couple lines and annotated pixel distances:

overlay

As you can see, the largest deformation of the bottom of the torso is 159/1072 ~= 0.148 - a little larger than the internally reported max deviation but close enough. I'm using the length of the green line as the errors are computed based on the longest axis of the input mesh, which in this case is measuring mesh top-to-bottom.

So, the error appears to be correct. 15% is an extreme amount of deformation, and that's exactly what's happening here.

hybridherbst commented 5 months ago

Thanks for the detailed explanation, well appreciated!

Would it be correct to say that this means the maximum error that can happen is typically 50% — at that error percentage, the entire mesh would be collapsed to a point in the center? (feels a bit counter-intuitive, but could explain why the error here „feels“ larger than just 15% — it’s actually 30% of the radius)

zeux commented 5 months ago

Err yes sorry, by “radius” I meant diameter. And yeah 50% practically speaking means the mesh could be decimated into a point in the center.