GLB Asset Surgery: 68% Smaller Textures With Geometry Untouched
GLB compressionmodel size reductiontexture compressionbase color palette quantizationPSNR verificationmodel LOD100-face low poly budget3D asset pipelinebrowser 3D loading optimizationmeshopt
There's a class of 3D problem no error ever surfaces: the model still displays and still animates — it's just too big. Loading drags on for seconds, the progress bar sticks at 90%, mobile devices occasionally crash. The root of these symptoms usually isn't code, it's the asset file itself. This article describes an approach already running inside an upload pipeline: no remodelling, no geometry changes, just "surgery" on the GLB's JSON and binary layers, compressing textures by purpose, measuring 68%~92% size reduction, and verifying the visual difference down to the pixel with PSNR. This toolchain is built into the Genesis foundation — models get processed automatically on upload, and you don't run scripts by hand.
One: First Separate What Can Be Compressed From What Breaks
Teams often compress models with one uniform setting, ending up either with blurry output or almost no reduction. The problem is that a model's assets are categorized, and the same compression setting should not apply to all of them.
A GLB file is essentially two things: a JSON chunk (structure: nodes, meshes, materials, texture references, accessor declarations) and a BIN chunk (the actual bytes: vertices, normals, UVs, indices, texture pixels). All the room for surgery lives in these two.
Textures must be handled by purpose tier — this is the single most important judgment in the whole approach:
- Normal maps and occlusion maps: this data feeds samplers that compute direction and occlusion. Human eyes are sensitive to their precision and tolerant of their compression. The approach is lossless re-encoding — not a pixel changes, and size drops through a different encoding scheme.
- Base color textures: this is the bulk of the size, and also where lossy handling is most acceptable. The approach is palette quantization — when color counts are limited, a good-quality palette cuts the data volume substantially.
Verification: 31.4dB PSNR with 0.35% differing pixels. These two numbers must be read together. A PSNR value alone is easy to misread; the differing-pixel ratio is the direct answer to "how much actually changed."
Two: Why Rewrite the GLB Instead of Re-exporting
Re-exporting is the other route, and it has two practical problems. First, it needs source files (.blend, .fbx), and plenty of models arrive with only a GLB. Second, re-exporting rewrites the author's scene setup, constraints, and modifiers — and models sometimes come back deformed. But the GLB is itself a complete scene description: it carries node hierarchy, material parameters, and animation tracks. Operating directly on it, without touching geometry, hierarchy, or animation, means changing only the bytes that should change.
The implementation idea in one line: rewrite the GLB using JSON + BIN surgery, with zero loss of geometry. That's the technical floor of the whole toolchain.
Three: The Hard Part Is Computing Offsets Before You Touch the Binary
Texture compression isn't the hard part. The hard part is the GLB binary layout — changing a texture means every offset has to be recomputed, and getting one byte wrong makes the file unreadable.
Layout problems that have to be handled:
- bufferView reordering — after lengths change, originally contiguous views must be rearranged or every later offset misaligns.
- accessor index remapping — when a view moves, the accessor declaring which byte range to read must follow.
- meshopt extension byteOffset shifting — meshopt-compressed models carry an extra offset layer that must be shifted separately, or the decompressed data is wrong.
- Multi-buffer layouts — a GLB may hold several buffer chunks (common after a GLB split), and each needs offsets computed independently.
There's no shortcut for that work. The discipline is compute first, write second, verify at every step.
Four: Five Gates — Skip Rather Than Write a Broken File
The worst outcome of binary surgery isn't a failure; it's writing a model that loads but renders wrong. When an artist gets such a file, they usually only discover it inside the scene, and tracing it back is painful. So this toolchain's principle is: if any step isn't confident, skip it and report — never write to disk.
The five gates:
- Mesh name validation — confirm the surgery target is the intended mesh, and don't cut at a similarly-named one.
- Compression validation — confirm the data was genuinely re-encoded rather than silently skipped.
- Multi-buffer validation — check every chunk under multi-buffer layouts instead of assuming one chunk.
- Geometry consistency validation — geometry data must be identical before and after surgery; this is the machine-checked version of the "geometry untouched" floor.
- Read-back validation — after writing, read and render the file again to confirm it works.
The last one is the real backstop: not "I think I wrote it right" but "I read it back and confirmed." In the numbers, variant texture stripping ran 215 items with 0 failures — and that zero is what all five gates together buy you.
Five: Face Count Is a Different Job — Low Poly Needs an Absolute Guarantee
With textures compressed, size still has another component: triangles. It's worth being clear about who handles what, since texture surgery doesn't touch geometry.
Face count belongs to another toolchain: gltfpack -sa plus a self-built greedy edge-collapse fallback. Its output target isn't "as small as possible" but a promisable specification: all 118 low-poly variants come in at ≤100 faces.
Why pin an absolute number? Because "near-zero cost at distance" needs a number you can write into a spec, not "most models got smaller." The part that exceeds the topology floor is handled by the self-built collapse tool — built precisely for models that toolchain can't reduce.
There's a companion optimization that's easy to miss: variants borrow the high-poly materials. LOD variants don't copy textures; they share the same Texture object (assertions measured at 33/33 and 36/36). That means "a low-poly variant now exists" costs almost no extra VRAM — otherwise a three-tier LOD would eat the memory budget.
Six: The Numbers
| Asset | Before | After | Reduction |
|---|---|---|---|
| Desk (base color quantization) | 25.74 MB | 8.22 MB | −68.1% |
| 20.7 MB multi-buffer 4K model | 20.7 MB | 4.38 MB | −92.5% |
| LOD variant texture stripping | 215 items | 0 failures | — |
| Low-poly face guarantee | — | 118/118 ≤100 faces | Promisable spec |
Quality-side verification: 31.4dB PSNR, 0.35% differing pixels. Geometry-side: before/after geometry consistency is machine-verified by gate 4.
Seven: What This Approach Actually Saves
Not "68% of money," but three more concrete things:
- It runs inside the upload pipeline, not as a pile of manual scripts. Models are processed automatically on upload, so every new asset gets treated — instead of relying on someone remembering to run it. The failure mode of a manual process is mundane: someone forgets.
- It doesn't promise what it can't keep. Normal maps go lossless and only base color goes lossy — and that tiering is baked into the tool's default behavior, so users don't have to remember which texture is safe to compress.
- Its verification is reproducible. PSNR and differing-pixel ratios come from a script, not from "I took a look and it's fine." The value of that gate: an artist gets a file that is either good or blocked — never "probably usable."
Eight: The Limits of These Numbers
- The reduction figures come from our specific assets. Your texture composition differs and so will the reduction; what's reusable is the tiering principle and the surgical process, not these two percentages.
- Untouched geometry is an intentional trade-off. If a model's problem is triangle count rather than texture size, texture surgery doesn't help — that belongs to face-count reduction, a separate toolchain.
- 0.35% differing pixels is not "no difference." Base color textures went through quantization, so pixels did change. The honest claim is that the difference is small and quantifiable, not that it's lossless.
- Lossless re-encoding depends on the source texture quality. For a texture that was already compressed once, lossless re-encoding has little room left.
FAQ
Q: Why not just decimate and compress inside Blender?
A: Two reasons. Many models arrive with only a GLB and no source file; and re-exporting also alters scene setup, so models can come back deformed. GLB surgery only touches the bytes that should change — geometry and hierarchy stay untouched.
Q: Will the images look blurry after compression?
A: They're handled by type. Normal and occlusion maps get lossless re-encoding with not a pixel lost; only base color textures go through palette quantization, and immediately after quantization we produce PSNR and differing-pixel ratios to verify. Our figures for that tier are 31.4dB with 0.35% differing pixels.
Q: Why skip instead of writing anyway?
A: Because a broken file is harder to trace than a missing one. Write a model that loads but renders wrong and the artist finds it in the scene, with a long way back to the cause. Skip and report it, and the only cost is that one asset went uncompressed — while the problem stays immediately visible.
Q: Do I have to build this toolchain myself?
A: No. It's already productized into the upload pipeline and applies automatically when a model is uploaded.
Source & Repository
All three addresses host identical content; the first two are faster within mainland China. The repository includes deployment docs and the acceptance scripts.
- Gitee (faster in mainland China): https://gitee.com/miduoxinxijeji/miduo.git
- GitCode (mirror): https://gitcode.com/qq_35054471/virtual-world
- GitHub: https://github.com/miduo100/3d-virtual-world
About Genesis
Genesis is a self-hosted 3D virtual world system built on Three.js + WebGL, helping individuals and businesses build their own 3D spaces. Accessible directly from a browser, compatible with both PC and mobile, it supports multiplayer online, federated teleportation, a shop system, and Agent integration—where an AI can enter your world as an embodied character. Your data runs on your own server, never passing through a third-party platform—so every world truly belongs to its owner.
Want every model uploaded into your world to slim down automatically, instead of relying on someone remembering to compress it? Genesis Virtual World CRM is a Three.js 3D virtual world foundation you deploy on your own server — GLB asset surgery, three-tier LOD, tiered texture compression, and variant material sharing are built into the upload pipeline, so you write the top layer. The official site (search "Genesis Virtual World CRM") has a demo world you can walk through.
About the name: Genesis in this article refers to Genesis Virtual World CRM — they are the same self-hosted 3D virtual world product. If "Genesis" doesn't turn us up in search, search "Genesis Virtual World CRM" instead.