# Vrm Mtoon Outline

> Adjusting VRM MToon material outline thickness in three-vrm. Covers outline width modes, property access, and how to fix outlines that appear too thick when models are scaled.

- Skill: `project-n-e-k-o/vrm-mtoon-outline` (Agent Skill)
- Install (CLI): `npx skillmds@latest add project-n-e-k-o/vrm-mtoon-outline`
- Raw SKILL.md: https://api.skillmd.com/api/skills/project-n-e-k-o/vrm-mtoon-outline/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: project-n-e-k-o (https://skillmd.com/u/project-n-e-k-o)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/project-n-e-k-o/vrm-mtoon-outline

---


# VRM MToon Outline Thickness Adjustment

This skill covers how to adjust outline thickness for VRM models using MToon materials in `@pixiv/three-vrm`.

## Common Symptoms

1. **Outline too thick** when model is scaled up or camera is close
2. **Outline thickness inconsistent** at different zoom levels
3. **Need to adjust outline programmatically** at runtime

---

## Key Concepts

### Outline Width Modes

MToon materials have two main outline width modes:

| Mode | Description | Behavior |
|------|-------------|----------|
| `'worldCoordinates'` | Outline width is a physical world-space size | Outline appears **thicker when model is scaled up** or camera is closer |
| `'screenCoordinates'` | Outline width is relative to screen pixels | Outline **stays consistent size** regardless of zoom/scale |

### Critical Properties

```javascript
material.outlineWidthMode    // 'none' | 'worldCoordinates' | 'screenCoordinates'
material.outlineWidthFactor  // Number - the actual width value
material.isMToonMaterial     // Boolean - true for MToon materials
material.needsUpdate         // Set to true after modifying properties
```

---

## Solution: Switch to Screen Coordinates

To make outlines stay consistent regardless of zoom/scale:

```javascript
function adjustOutlineThickness(vrm, screenFactor = 0.005) {
    vrm.scene.traverse((object) => {
        if (object.isMesh || object.isSkinnedMesh) {
            const materials = Array.isArray(object.material) ? object.material : [object.material];
            
            materials.forEach(material => {
                if (!material || !material.isMToonMaterial) return;
                
                // Check if this material has outlines enabled
                const hasOutline = material.outlineWidthFactor > 0 && 
                                   material.outlineWidthMode !== 'none';
                
                if (hasOutline) {
                    // Switch to screen coordinates mode
                    material.outlineWidthMode = 'screenCoordinates';
                    material.outlineWidthFactor = screenFactor;
                    material.needsUpdate = true;
                }
            });
        }
    });
}
```

### Factor Value Guidelines

For `screenCoordinates` mode:

| Factor Value | Approximate Effect |
|-------------|-------------------|
| `0.002 - 0.003` | Very thin outline (1 pixel) |
| `0.005` | Thin outline (1-2 pixels) |
| `0.01` | Medium outline (2-3 pixels) |
| `0.02+` | Thick outline |

---

## Detection Pattern

To diagnose outline issues, log all materials:

```javascript
function debugOutlineMaterials(vrm) {
    let count = 0;
    vrm.scene.traverse((object) => {
        if (!object.isMesh && !object.isSkinnedMesh) return;
        
        const materials = Array.isArray(object.material) ? object.material : [object.material];
        materials.forEach(material => {
            if (material?.isMToonMaterial || 'outlineWidthFactor' in material) {
                count++;
                console.log({
                    name: material.name || '未命名',
                    type: material.type,
                    isMToonMaterial: material.isMToonMaterial,
                    outlineWidthMode: material.outlineWidthMode,
                    outlineWidthFactor: material.outlineWidthFactor
                });
            }
        });
    });
    console.log(`Found ${count} MToon/Outline materials`);
}
```

---

## Important Notes

> [!IMPORTANT]
> **Call timing matters!** The function must be called AFTER `currentModel` is set. In `vrm-core.js`, the `loadModel()` function sets `manager.currentModel` near line 923. Any function that accesses `currentModel` must be called after this point.

> [!NOTE]
> Materials named `"XXX (Outline)"` are outline pass materials automatically created by three-vrm. They share properties with the main material but render the outline effect.

---

## API Reference (three-vrm MToon)

The enum values for `outlineWidthMode`:

```javascript
// From three-vrm.module.min.js
{
    None: "none",
    WorldCoordinates: "worldCoordinates", 
    ScreenCoordinates: "screenCoordinates"
}
```

| Property | Type | Description |
|----------|------|-------------|
| `outlineWidthMode` | string | Width calculation mode |
| `outlineWidthFactor` | number | Width value (meaning depends on mode) |
| `outlineColorFactor` | Color | Outline color |
| `outlineLightingMixFactor` | number | How much lighting affects outline |
| `outlineWidthMultiplyTexture` | Texture | Texture to modulate outline width |

