donmccurdy/glTF-Transform

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

Closed this issue · 1 comments

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:

  • Version: 3.10.1
  • Environment: cli

Additional context

Sphere.zip

input

image

output

image

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:

  • error = 0.45 -> tetrahedron
  • error = .50 -> pair of back-to-back triangles
  • error = .55 -> empty primitive

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.