---
title: "From Blender to Launch: An End-to-End Compression Pipeline"
description: "Take a 50MB PBR model through the full pipeline, from Blender export to KTX2 textures and engine loading, and get it under 5MB. Scripts and cheat sheet included."
url: "https://any3d.cc/en-US/blog/end-to-end-compression-pipeline"
markdown: "https://any3d.cc/llm/blog/end-to-end-compression-pipeline.md"
date: "2026-06-26"
tags: ["3d-compression","pipeline","texture-compression","vertex-compression","gltf"]
---

# From Blender to Launch: An End-to-End Compression Pipeline

By now we've covered the tools, theory, and decision-making. In this final post, we'll stitch everything together into a real, runnable pipeline: starting from a real Blender model, compressing step by step, tracking file size, VRAM, and load time, and seeing if we can turn a 50MB beast into a 5MB model that loads instantly on a phone.

Target audience: those who've read the first 5 posts and are ready to do it for real. No new concepts here—just a repeatable process, commands, and scripts.

## Starting Point: A Real PBR Model

Let's use a typical e-commerce product showcase model: a high-precision product with full PBR textures.

| Initial Metric                         | Value                                                            |
| -------------------------------------- | ---------------------------------------------------------------- |
| Blender source file                    | ~120MB (includes unexported high-poly)                           |
| Exported GLB (float32 + PNG)           | **~50MB**                                                        |
| Vertex count                           | ~180k                                                            |
| Textures                               | 6x 4096×4096 (albedo, normal, roughness, metallic, AO, emissive) |
| VRAM usage (all textures decompressed) | **~520MB**                                                       |
| Target                                 | File ≤ 5MB, VRAM manageable, instant mobile load                 |

A 50MB file with 520MB of VRAM—this model would crash any mobile device. Let's go step by step.

## Step 0: Exporting Correctly from Blender

The first compression checkpoint is actually the export itself. Many people lose blood here.

Key settings when exporting glTF from Blender:

- **Format**: `glTF Binary (.glb)` (single file, easier to transfer)
- **Geometry**: Check `Normals`, `Tangents` (PBR normal maps need tangents)
- **UV**: Ensure they're exported (on by default)
- **Textures**: `Automatic` or `JPEG` (texture format doesn't matter now—we'll recompress later, but make sure they're exported)
- **Compression**: **Do not** check Blender's built-in Mesh compression; we'll use more professional tools
- **Transform**: `+Y Up` (glTF standard)
- **Data**: Only export what you need (animations, cameras, lights—if not needed, don't export them and reduce size)

After export, `model.glb`: **50MB**, 6 PNG textures, float32 vertices. This is our baseline.

> Common pitfall: Blender exports unused meshes, hidden helper objects, etc. by default. Before exporting, run `File > Clean Up > Purge Orphans` and select only the objects you want to export in the outliner.

## End-to-End Pipeline Overview

Let's visualize the whole pipeline:

```
Blender source file
   │   Export .glb (float32 + PNG)              50MB
   ▼
[1] Deduplication + Weld duplicate vertices (gltf-transform)   ~45MB
   │
[2] Vertex compression: MeshOpt (gltfpack / gltf-transform)   ~30MB
   │
[3] Texture compression: PNG → KTX2 (ETC1S/UASTC)             ~6MB
   │
[4] (Optional) Geometry simplification LOD (simplify)         ~4-5MB
   ▼
Final model-final.glb                                          ~5MB
   │
Engine loading (Three.js / Babylon.js) → runtime transcoding → Deploy
```

The numbers for each step will be tracked in the table below.

## Toolchain: Which One to Choose

Several compression tools are available. Let's compare them so you don't pick the wrong one:

| Tool                       | Strengths                                              | Weaknesses                                | Best For                                        |
| -------------------------- | ------------------------------------------------------ | ----------------------------------------- | ----------------------------------------------- |
| **gltf-transform**         | All-in-one, textures + vertices, scriptable            | Not the absolute best compression ratio   | **Recommended primary tool** for most scenarios |
| gltfpack                   | Professional vertex compression, native MeshOpt        | Weak texture compression                  | Vertex-heavy models, need fine MeshOpt control  |
| toktx                      | Most professional texture compression, full parameters | Textures only, not whole models           | Fine-tuning single textures                     |
| gltf-pipeline              | Old-school, supports Draco                             | Not actively maintained, limited features | Existing Draco projects                         |
| Online tools (gltf.report) | Zero installation                                      | Not suitable for automation, batch jobs   | Experiments, one-off tasks                      |

**Main recommendation**: Use `gltf-transform` for the entire pipeline. Supplement with `gltfpack` for vertices and `toktx` for individual textures when needed. All steps below use gltf-transform.

## Step 1: Deduplication + Weld

Models often have duplicate vertices, unused nodes, and materials. Clean them up first.

```bash
gltf-transform optimize model.glb step1.glb --weld --prune
```

| Stage        | File Size | VRAM   | Change                                      |
| ------------ | --------- | ------ | ------------------------------------------- |
| Baseline     | 50MB      | ~520MB | —                                           |
| Step 1 Dedup | 45MB      | ~520MB | -5MB (VRAM unchanged, textures still there) |

VRAM barely moved—expected. Deduplication mainly saves vertices and structure; textures are the VRAM hog.

## Step 2: Vertex Compression with MeshOpt

```bash
gltf-transform optimize step1.glb step2.glb --meshopt --weld --prune
```

`--meshopt` quantizes vertices to 16-bit and uses MeshOpt lossless encoding, automatically adding the `EXT_meshopt_compression` extension.

| Stage            | File Size | VRAM   | Change              |
| ---------------- | --------- | ------ | ------------------- |
| Step 1           | 45MB      | ~520MB | —                   |
| Step 2 + MeshOpt | 30MB      | ~520MB | -15MB (vertex part) |

VRAM still ~520MB? Yes—because vertices take up a small portion of VRAM (10-20%). Cutting vertices has limited impact on VRAM. The real VRAM monster is textures, which we tackle next.

## Step 3: Texture Compression PNG → KTX2

This step is the biggest bang for your buck.

```bash
gltf-transform optimize step2.glb step3.glb \
  --texture-compress basisu \
  --meshopt --weld --prune
```

`--texture-compress basisu` automatically determines each texture: color textures (albedo, emissive) use ETC1S, data textures (normal, roughness, metallic, AO) use UASTC.

| Stage         | File Size | VRAM      | Change                       |
| ------------- | --------- | --------- | ---------------------------- |
| Step 2        | 30MB      | ~520MB    | —                            |
| Step 3 + KTX2 | 6MB       | **~70MB** | -24MB file / **-450MB VRAM** |

This step is the turning point of the entire pipeline:

- File size drops from 30MB to 6MB
- **VRAM drops from 520MB to ~70MB**—because the 6 4096 textures go from "decompressed raw pixels" to "block compressed", each dropping from ~87MB to ~11-14MB

VRAM is reduced by an order of magnitude. This is the key to whether a mobile device can handle it.

## Step 4: (Optional) Geometry Simplification

If you want even smaller, and the scene can tolerate lower vertex precision, add geometry simplification.

```bash
gltf-transform optimize step3.glb final.glb \
  --texture-compress basisu \
  --meshopt \
  --simplify --simplify-ratio 0.5 \
  --weld --prune
```

`--simplify-ratio 0.5` means keep about 50% of the vertices.

| Stage                 | File Size | VRAM  | Change                         |
| --------------------- | --------- | ----- | ------------------------------ |
| Step 3                | 6MB       | ~70MB | —                              |
| Step 4 + simplify 0.5 | 4.5MB     | ~70MB | -1.5MB (VRAM nearly unchanged) |

Simplification mainly saves file size, with little impact on VRAM. The trade-off is reduced model detail—noticeable at close range. For e-commerce product pages, over-simplification is usually not recommended; it's great for architecture/large scenes.

## Full Progress Tracking Table

Let's stack all four steps for a complete picture (based on the sample model, numbers are illustrative):

| Step                      | File Size | VRAM   | Cumulative Reduction      |
| ------------------------- | --------- | ------ | ------------------------- |
| Baseline (float32 + PNG)  | 50MB      | ~520MB | —                         |
| + Dedup + Weld            | 45MB      | ~520MB | -10%                      |
| + MeshOpt vertices        | 30MB      | ~520MB | -40%                      |
| + KTX2 textures           | 6MB       | ~70MB  | **-88% file / -87% VRAM** |
| + Geometry simplify (0.5) | 4.5MB     | ~70MB  | **-91% file**             |

Conclusion: **Texture compression provides the vast majority of file size and VRAM savings.** Vertex compression is the icing on the cake; texture compression is the lifesaver. This perfectly matches the conclusion from Part 1—textures make up 80% of the volume, so optimizing them yields the highest return.

## One-Command Version: Lazy All-in-One

If you don't want to do it step by step, apply all optimizations at once:

```bash
gltf-transform optimize model.glb model-final.glb \
  --texture-compress basisu \
  --meshopt \
  --simplify --simplify-ratio 0.5 \
  --weld --prune
```

This single command = dedup + weld + MeshOpt vertices + KTX2 textures + geometry simplification. It covers 90% of scenarios. The step-by-step approach is mainly for understanding and tuning parameters.

## Don't Want to Set Up an Environment? Use Any3D Online Compression

The above gltf-transform commands and scripts require installing Node, configuring the toolchain, and remembering a bunch of parameters. **Any3D's [online compression tool](/tools/model-compression) eliminates all that**:

- **No need to download scripts or set up an environment**—just open a web page and use it
- **Model never leaves your device**—everything is processed locally in the browser
- **Visual configuration**—texture KTX2, vertex MeshOpt, geometry simplification—sliders for parameters, real-time preview
- **One-click multi-platform**—export compressed results for mobile / desktop / VR

Pick a GLB, choose a target platform, click, and get the compressed model. Under the hood it's the same engine as the command line (gltf-transform), but with zero learning curve—no terminal required.

## Common Pitfalls in Practice

**Model turns black / textures don't show after compression**

- 99% of the time it's color space: color textures missing sRGB. In Three.js: `texture.colorSpace = THREE.SRGBColorSpace`.
- When using toktx, forgot `--srgb` for color textures.

**Normal map lighting is wrong after compression**

- Normal map was encoded with ETC1S; switch to UASTC.
- Normal map is in DirectX style (green channel pointing down), but the engine expects OpenGL style; need to flip the G channel.

**Mobile loading hangs on the first screen**

- Check if you're loading the Draco decoder wasm (extra request). Prefer MeshOpt for mobile.
- KTX2 transcoder path is misconfigured, causing fallback to CPU decompression.

**Compressed file is larger than the original**

- Texture is too small (< 128px). KTX2 isn't cost-effective for small textures due to block compression overhead.
- Model was already compressed; recompressing offers no benefit (or negative benefit).

**Model breaks after simplification**

- `--simplify-ratio` is too low; try 0.7-0.8.
- Simplification works well for hard surfaces (mechanical, architectural) but can break organic surfaces (characters).

**KTX2 fails to load in some browsers**

- Old Safari / old WebView don't support it. Prepare a PNG/WebP fallback, or use the `fallback` field of `KHR_texture_basisu` to provide backup textures.

## Series Cheat Sheet

The essence of all 6 posts condensed into a single table. Bookmark this.

### Volume Breakdown

| Component      | Percentage | Optimization Tool              |
| -------------- | ---------- | ------------------------------ |
| Textures       | 70-85%     | KTX2 (biggest gain)            |
| Vertex data    | 10-20%     | MeshOpt / Quantization / Draco |
| Animation data | 0-15%      | Reduce keyframes / compress    |
| Other          | < 2%       | Deduplication                  |

### VRAM Formula

```
Traditional format VRAM = width * height * 4 bytes * 1.333 (with mipmaps)
KTX2 block compressed VRAM ≈ above / 4 (ETC1S) or / 2 (UASTC)
```

### Vertex Compression Selection

| Scenario                                       | Recommended                                 |
| ---------------------------------------------- | ------------------------------------------- |
| Zero dependencies, simplest                    | Pure quantization (`KHR_mesh_quantization`) |
| Web balanced first choice                      | MeshOpt                                     |
| Extreme compression ratio, can wait for decode | Draco                                       |
| Mini programs / package-sensitive              | Pure quantization / MeshOpt, avoid Draco    |

### Texture Compression Selection

| Texture Type                              | Recommended Codec                |
| ----------------------------------------- | -------------------------------- |
| albedo / emissive (color)                 | KTX2 ETC1S                       |
| normal / roughness / metallic / AO (data) | KTX2 UASTC                       |
| Desktop web, prioritize download speed    | WebP / AVIF                      |
| Small textures (< 128px)                  | Keep PNG, don't compress to KTX2 |

### One-Click Command

```bash
# Full optimization (textures + vertices + simplification)
gltf-transform optimize model.glb model-final.glb \
  --texture-compress basisu --meshopt \
  --simplify --simplify-ratio 0.5 --weld --prune
```

### Platform Quick Reference

| Platform      | Textures           | Vertices               |
| ------------- | ------------------ | ---------------------- |
| Desktop Web   | WebP / KTX2        | MeshOpt                |
| Mobile Web    | **KTX2 mandatory** | MeshOpt                |
| VR            | **KTX2 mandatory** | MeshOpt + LOD          |
| Mini programs | KTX2 / WebP        | MeshOpt / Quantization |
| Large scenes  | **KTX2 mandatory** | MeshOpt + Draco + LOD  |

## Series Recap

Six posts take you through a complete chain:

1. **[Why Are 3D Models So Big?](/blog/why-3d-models-are-heavy)**: Understand volume composition and VRAM reality
2. **[First Lesson in Model Slimming: Vertex Compression's Three Techniques](/blog/vertex-compression-basics)**: How quantization, MeshOpt, and Draco work, and how to choose between them
3. **[Textures: The VRAM-Hungry Gluttons You Never Saw Coming](/blog/texture-vram-problem)**: Why PNG/JPG are guilty in the GPU's eyes
4. **[KTX2 in Practice: The Right Way to Use Texture Compression](/blog/ktx2-practice)**: ETC1S vs UASTC selection, toolchains, and engine loading
5. **[Different Platforms, Different Fates: A Guide to Choosing Compression Schemes](/blog/compression-selection-guide)**: A decision framework by platform and scenario
6. **This Post**: The end-to-end pipeline, from Blender to launch

The core message in one line: **figure out the bottleneck first (download / VRAM / frame rate), then pick your tools; texture compression brings the biggest gain, vertex compression is the icing on the cake, and different platforms play by different rules, so don't apply one formula to everything.**

Follow the scripts and cheat sheet in this post, and taking a model from 50MB down to 5MB (with VRAM dropping from 520MB to 70MB) becomes a repeatable path. The rest is just doing it.
