donmccurdy / glTF-Transform

glTF 2.0 SDK for JavaScript and TypeScript, on Web and Node.js.
https://gltf-transform.dev
MIT License
1.3k stars 145 forks source link

Simplification with `ratio: 0 error: 1` does not simplify enough #1326

Closed marwie closed 3 months ago

marwie commented 3 months ago

Describe the bug It seems like I'm currently not able to simplify a mesh to the quality level that I want. In my example I have a sphere and try to simplify it with extreme values - however the resulting mesh still has more vertices than desired. I'd like to better understand why that is and what factors are responsible for the results

To Reproduce Steps to reproduce the behavior:

  1. gltf-transform weld --tolerance 0.0001 input.glb weld.glb
  2. gltf-transform simplify --error 1 --ratio 0 weld.glb simplify.glb --verbose --lock-border
  3. Mesh still has 92 vertices (-82.00 %) -

Expected behavior Expected the simplifier to produce a mesh with maybe one triangle?

Versions:

Additional context

Sphere.zip

input

image

output

image

donmccurdy commented 3 months ago

Testing with this script in https://gltf.report ...

import { MeshoptSimplifier } from 'meshoptimizer';
import { weld, simplify } from '@gltf-transform/functions';

await document.transform(
    weld({tolerance: 0.0001}),
    simplify({simplifier: MeshoptSimplifier, ratio: 0.0, error: 0.45, lockBorder: true})
);

... I get pretty much the same result. Note that MeshoptSimplifier does not see all of a mesh's attributes, just the vertex positions and their connectivity (via indices). In general, and particularly with lockBorder, it's going to try to preserve topology, so I think collapsing a 3D shape to a single triangle is probably something the algorithm explicitly avoids.

What we can do to give Meshopt the best possible chance of success, would be to minimize seams and maximize connectivity. Welding tries to do that, but extra vertex attributes make it hard. I'm not sure which attributes you need at higher LODs, but removing everything but POSITION resolves the issues you're seeing...

import { MeshoptSimplifier } from 'meshoptimizer';
import { weld, simplify, prune } from '@gltf-transform/functions';

for (const mesh of document.getRoot().listMeshes()) {
    for (const prim of mesh.listPrimitives()) {
        for (const semantic of prim.listSemantics()) {
            if (semantic !== 'POSITION') {
                prim.setAttribute(semantic, null);
            }
        }
    }
}

await document.transform(
    prune(),
    weld({tolerance: 0.0001}),
    simplify({simplifier: MeshoptSimplifier, ratio: 0.0, error: 0.45, lockBorder: true})
);

... with that simplification gets the result you're probably expecting:


Possibly a weld mode that explicitly ignores vertex attributes other than position would help here (but doesn't exist in glTF Transform now) if you don't want to delete these attributes. There's also an experimental meshopt simplification function that includes vertex attributes — with custom weights — but that isn't integrated into glTF Transform yet, see #1153.