Modell-Diät, Lektion 1: Die drei Waffen der Vertex-Kompression
Im vorherigen Artikel haben wir eine GLB-Datei geöffnet und gesehen, dass Texturen 80 % der Größe beanspruchen, während Vertices nur 10-20 % ausmachen. Ist Vertex-Kompression also irrelevant?
Ganz im Gegenteil. Wenn die Texturen eines Modells bereits zu KTX2 komprimiert sind und die Vertices dicht gepackt sind, sind die verbleibenden 20 % die Vertices – und diese 20 % lassen sich nochmal halbieren oder sogar um 90 % reduzieren. Noch wichtiger: Vertex-Kompression ist eine der wenigen Optimierungen, die fast kostenlos ist und sofort wirkt – ein paar Befehle, ein Decoderwechsel, und die Datei ist schlanker.
Dieser Artikel klärt drei Dinge: wie Vertex-Daten wirklich aussehen; das Temperament der drei Ansätze (Quantisierung, MeshOpt, Draco); und eine Erkenntnis, die Sie vor Fallstricken bewahrt – es gibt keine „beste" Lösung, nur die „passendste" Lösung.
Wie groß ist ein Vertex
Zuerst, was ist in einem Vertex enthalten. In glTF besteht jeder Vertex aus mehreren Attributen:
| Attribut | Zweck | Standardpräzision | Bytes pro Vertex |
|---|---|---|---|
| position (Position) | Vertex-Koordinaten im Raum | 3 × float32 | 12 |
| normal (Normale) | Bestimmt die Lichtrichtung | 3 × float32 | 12 |
| tangent (Tangente) | Normal-Map-Berechnung | 4 × float32 | 16 |
| texcoord_0 (UV) | Textur-Sampling-Koordinaten | 2 × float32 | 8 |
| color (Vertex-Farbe) | Per-Vertex-Shading | 4 × float32 | 16 |
Ein Vertex mit dem vollständigen PBR-Attribut-Set belegt 48-64 Bytes allein für Geometrie. Ein Modell mit 100.000 Vertices ist allein durch Vertices 5-6 MB groß.
Fast alles hier verwendet float32 (32-Bit-Gleitkommazahlen). Das ist die Standardeinstellung und gleichzeitig die Angriffsfläche der Vertex-Kompression – denn die allermeisten Attribute benötigen schlicht keine 32-Bit-Präzision.
Waffe eins: Quantisierung
Quantisierung ist das zugrunde liegende Prinzip aller Vertex-Kompression; Draco und MeshOpt setzen sie intern ebenfalls ein.
Quantisierung (Mapping von hochpräzisen Floats auf niedrigpräzise Integer) läuft darauf hinaus: Für den Float 3.14159265 reicht es, sich 3.14 zu merken. Für einen Satz von Koordinaten innerhalb eines Raums verwendet man statt 32-Bit-präziser Erfassung jeder Dezimalstelle einen Integer mit kleinerem Wertebereich.
Original: position.x = 1.234567 (float32, 4 Bytes)
Quantisert: position.x = 1234 (int16, 2 Bytes) + ein scale/offset zur Wiederherstellung
Vorher vs. nachher:
| Attribut | float32 Bytes | Quantisiert (16-Bit) | Ersparnis |
|---|---|---|---|
| position | 12 | 6 | 50% |
| normal | 12 | 6 (oder 4, mit int8 + octahedral) | 50-67% |
| tangent | 16 | 4-8 | 50-75% |
| texcoord | 8 | 4 | 50% |
Für den 48-64-Byte-Vertex komprimiert Quantisierung im Wesentlichen auf 16-24 Bytes, also mehr als halbiert.
Wann Quantisierung einsetzen
- Sie wollen nur die Größe reduzieren und brauchen nicht das maximale Kompressionsverhältnis
- Sie wollen null Decoder-Abhängigkeiten – ein quantisiertes glTF nutzt die Standard-Erweiterung
KHR_mesh_quantization, die von gängigen Engines nativ unterstützt wird, ohne zusätzliche Decoder-Bibliothek - Ihre Zielplattform ist paketgrößenempfindlich (z. B. WeChat Mini Programs, wo ein Draco-Decoder zig KB kostet)
Wann nicht einsetzen
- Das Modell ist winzig und Detail ist das Verkaufsargument (z. B. industrielle Bauteile im Millimeterbereich). Quantisierung verrät sich gerade bei kleinen Modellen – Texturen mögen noch gehen, aber eine Vertex-Verschiebung von 0,1 mm ist im Close-up sichtbar.
Die wahren Kosten des Präzisionsverlusts: In einer Schmuck-Szene wurde ein Ring-Modell auf 16 Bit quantisiert, und die Metallkante zeigte im Close-up Aliasing. Die Ursache waren nicht zu wenige Vertices; der Welt-Raum war zu klein, als dass 16-Bit-Integer ihn fein genug ausdrücken könnten. Abhilfe schafft, den Quantisierungsbereich zu verkleinern (die Bounding-Box von
positionreduzieren) oder bei kleinen Modellen die Bittiefe zu erhöhen.
Waffe zwei: MeshOpt
MeshOpt ist die offizielle glTF-Erweiterung EXT_meshopt_compression, positioniert als „anständiges Kompressionsverhältnis, rasantes Dekodieren".
Vorgehen: zuerst die Attribute quantisieren (wie oben), dann eine Technik namens Entropie-Kodierung (lossless) anwenden, um die quantisierten Integer verlustfrei nachzukomprimieren. Mit anderen Worten: verlustbehaftete Quantisierung + verlustfreie Entropie-Kodierung = kleinere Größe, gleiche Qualität wie bei reiner Quantisierung.
- Kompressionsverhältnis: weitere 30-50 % kleiner als bei reiner Quantisierung
- Dekodiergeschwindigkeit: extrem schnell, reines C/JS, zig Millionen Vertices pro Sekunde auf einem Thread
- Decoder-Größe: winzig (~20-30 KB gezipt)
- Kompatibilität: von Three.js und Babylon.js nativ unterstützt, ein De-facto-Webstandard
Wann MeshOpt einsetzen
- Sie brauchen ein höheres Kompressionsverhältnis, können aber Dracos langsameres Dekodieren nicht akzeptieren
- Web/Mobile/WebXR stehen im Mittelpunkt – Dekodiergeschwindigkeit wirkt sich direkt auf den First-Paint aus
- Das Modell wird häufig dekodiert (z. B. dynamisch geladene Level)
Wann nicht einsetzen
- Ihre Zielplattform erkennt nicht einmal
EXT_meshopt_compression(selten, alte Engines) - Sie brauchen nur „es läuft" und der 30 %-Unterschied ist Ihnen egal – dann ist reine Quantisierung einfacher und hat eine Abhängigkeit weniger
Waffe drei: Draco
Draco ist Googles Kompressionslösung, positioniert für „maximales Kompressionsverhältnis".
Der wesentliche Unterschied zu den anderen beiden: Draco verändert die Konnektivität (Topologie) der Vertices. Quantisierung ändert nur die numerische Repräsentation jedes Vertex; MeshOpt fügt verlustfreie Kodierung hinzu; Draco reorganisiert das Dreiecksnetz und drückt „welche Vertices bilden Dreiecke" kompakter aus.
- Kompressionsverhältnis: das höchste der drei, oft 90 %+ Reduktion bei vertex-dichten Modellen
- Dekodiergeschwindigkeit: die langsamste der drei, aber absolut immer noch schnell
- Decoder-Größe: größer (~100-200 KB, meist als separates wasm geladen)
- Qualität: einstellbar, aber bei extremen Verhältnissen sieht man Verformungen
Wann Draco einsetzen
- Extrem große, vertex-super-dichte Modelle (Millionen-Vertex-Scans, Gelände)
- Einmaliges Laden, lange Wiederverwendung nach Dekodierung (langsameres Dekodieren akzeptabel)
- Paketgröße ist nicht der Flaschenhals, Download-Geschwindigkeit schon
Wann nicht einsetzen
- Mobil + schnelles First Paint nötig – man muss sowohl Decoder als auch Modell herunterladen, was ausbremst
- Strenge Paketgrößen-Umgebungen wie Mini Programs
- Modelle mit Skelett-Animation, Morph-Targets – Dracos Unterstützung dafür ist schwach, Fehlkonfiguration verursacht Probleme
Alle drei im Vergleich: eine Auswahl-Tabelle
Die Kompressionsverhältnisse unten referenzieren Community-Benchmarks (DeepKolos' Tests + Reddit r/threejs). Unterschiedliche Modelle variieren, aber die relativen Beziehungen sind stabil:
| Option | Kompressionsverhältnis (vs. float32) | Dekodiergeschwindigkeit | Decoder-Größe | Verlustbehaftet? | glTF-Erweiterung |
|---|---|---|---|---|---|
| Reine Quantisierung | ~50 % | Nativ, kein Dekodieren | 0 | Ja (Präzision) | KHR_mesh_quantization |
| MeshOpt | ~25-35 % | Extrem schnell | ~25 KB | Ja (Präzision) | EXT_meshopt_compression |
| Draco | ~10-20 % | Schnell (langsamste der drei) | ~100-200 KB | Ja (Präzision + Topologie) | KHR_draco_mesh_compression |
Decoder und Plattform-Kompatibilität:
| Plattform | Reine Quantisierung | MeshOpt | Draco |
|---|---|---|---|
| Desktop-Web | ✅ Nativ | ✅ Nativ | ✅ Decoder-Konfig nötig |
| Mobil-Web | ✅ Nativ | ✅ Nativ | ⚠️ Decoder schwer |
| WebXR/VR | ✅ Nativ | ✅ Empfohlen | ⚠️ Mit Vorsicht |
| WeChat Mini Program | ✅ Empfohlen | ✅ Empfohlen | ❌ Möglichst vermeiden |
Einzeiler: einfach und abhängigkeitsfrei → reine Quantisierung; ausgewogen → MeshOpt; maximales Verhältnis und Wartezeit ok → Draco.
Praxis: Quantisierung und MeshOpt mit gltfpack
gltfpack ist das offizielle glTF-Werkzeug; ein Befehl erledigt Quantisierung und MeshOpt.
Zuerst installieren (Binaries von den gltfpack-Releases):
# model.glb auf 16-Bit quantisieren und MeshOpt-Kompression hinzufügen
gltfpack -i model.glb -o model-packed.glb -cc
# -cc = compress (fügt EXT_meshopt_compression zur Standard-Quantisierung hinzu)
Häufige Parameter:
# Nur quantisieren, kein MeshOpt (am leichtesten, null Decoder-Abhängigkeit)
# gltfpack quantisiert Vertices standardmäßig auf 16 Bit (KHR_mesh_quantization),
# es ist kein extra Flag nötig
gltfpack -i model.glb -o model-quant.glb
# Quantisieren und MeshOpt aktivieren
gltfpack -i model.glb -o model-meshopt.glb -cc
# Bei sehr vielen Vertices kann man auch vereinfachen (reduziert Vertex-Zahl, verändert Modell)
gltfpack -i model.glb -o model-simplify.glb -cc -si 0.5
# -si 0.5 bedeutet Vereinfachung auf etwa 50 % der Vertices
Zu
-cc: Es ist der „compress"-Schalter, der zusätzlichEXT_meshopt_compressionanwendet.Ohne-ccquantisiert gltfpack trotzdem standardmäßig – das heißt,gltfpack -i in.glb -o out.glballein ist bereits „reine Quantisierung, null Decoder-Abhängigkeit". (-vist der ausführliche Logging-Schalter – nicht verwechseln.)
Typische Ergebnisse (ein 5-MB-, 120k-Vertex-PBR-Modell, nur als Referenz):
| Behandlung | Dateigröße | Hinweise |
|---|---|---|
| Original (float32) | 5.0 MB | Basis |
| Reine Quantisierung (Standard) | 2.6 MB | Halbiert, kein sichtbarer Unterschied |
MeshOpt (-cc) | 1.7 MB | Weitere 35 % gespart, minimal schneller |
Hinweis:
-si-Vereinfachung ist ein verlustbehafteter Vorgang, der die Geometrie verändert; er ist nicht dasselbe wie Kompression. Kompression versucht, die visuelle Treue zu bewahren; Vereinfachung entfernt aktiv Details. Beides lässt sich stapeln, hängt aber davon ab, ob die Szene es zulässt.
Häufige Fallstricke
- Normalenrichtung nach Quantisierung verändert: meist zu geringe Präzision. Für Normalen mindestens 16 Bit oder 8-Bit-Oktahedral-Kodierung verwenden.
- Materialien nach Draco-Dekodierung verloren: Draco komprimiert nur Meshes; Materialien und Texturen müssen separat behandelt werden. Beim Laden müssen sowohl der Draco-Decoder als auch die KHR-Erweiterungen konfiguriert sein.
- Draco lädt in einem Mini Program nicht: der Decoder-wasm ist in manchen Laufzeiten eingeschränkt; Wechsel zu MeshOpt behebt das meist.
- Modell „driftet" nach Quantisierung: Wenn das Modell weit vom Ursprung entfernt ist, kann 16-Bit-Präzision große Koordinaten und kleine Details nicht gemeinsam ausdrücken. Abhilfe: Modell vor dem Quantisieren in die Nähe des Ursprungs verschieben oder Bittiefe erhöhen.
Nächster Schritt
Vertices sind komprimiert – nicht zu früh feiern. Wie erwähnt, machen Texturen 80 % der Modellgröße aus. Als Nächstes wechseln wir das Schlachtfeld und betrachten, warum traditionelle PNG/JPG in den Augen der GPU ein „VRAM-Vielfraß" sind und wie GPU-native Texturformate das lösen.