---
id: wiki-20260508-vr-sickness-redir
title: VR Sickness
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [cybersickness, simulator sickness, VR motion sickness]
duplicate_of: none
source_trust_level: A
confidence_score: 0.91
verification_status: applied
tags: [vr, ux, perception, three-js]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript
  framework: Three.js / WebXR
---

# VR Sickness

## 매 한 줄
> **"매 visual motion 과 vestibular 의 mismatch 의 motion sickness 의 trigger"**. 매 VR 의 가장 큰 UX 장벽 — 매 frame rate / FOV / locomotion 의 careful design 의 mitigation. 2026 의 Quest 3 / Vision Pro / PCVR 의 90Hz+ 의 default — 매 여전히 design pattern 의 핵심.

## 매 핵심

### 매 원인
- **Sensory mismatch**: 매 eye 의 motion 의 perceive — 매 inner ear 의 stationary 의 report.
- **Low frame rate**: <72fps 의 judder 의 sickness 의 trigger.
- **Vection**: 매 large optical flow 의 self-motion 의 illusion.
- **Latency**: motion-to-photon >20ms 의 mismatch 의 amplify.
- **FOV motion**: peripheral 의 motion 의 sensitivity 가장 큼.

### 매 mitigation 기법
- **Teleport locomotion**: 매 smooth 대신 fade-to-black + jump.
- **Tunnel vision (vignette)**: 매 motion 시 peripheral mask — 매 vection 감소.
- **Snap turning**: 매 smooth rotation 대신 30° step.
- **Comfort settings**: 매 user 의 individual tuning.
- **High frame rate**: 90Hz+ 의 mandatory — Quest 3 의 default 90/120Hz.
- **Stable horizon**: 매 cockpit / fixed reference frame.

### 매 응용
1. Beat Saber — 매 stationary play 의 zero motion sickness.
2. Half-Life Alyx — 매 teleport + smooth 의 toggle.
3. 매 자전거 simulator — 매 physical motion 의 real vestibular alignment.

## 💻 패턴

### 매 Three.js + WebXR 의 framerate 의 monitor
```typescript
import * as THREE from 'three';

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.xr.enabled = true;

let lastTime = performance.now();
renderer.setAnimationLoop((time) => {
  const dt = time - lastTime;
  if (dt > 14) console.warn(`Frame drop: ${dt.toFixed(1)}ms`);  // 매 <72fps 의 경고
  lastTime = time;
  renderer.render(scene, camera);
});
```

### 매 Vignette 의 motion 시 적용
```glsl
// fragment shader
uniform float u_vignetteStrength;
varying vec2 vUv;

void main() {
  vec2 center = vUv - 0.5;
  float dist = length(center);
  float vignette = smoothstep(0.5, 0.3 - u_vignetteStrength * 0.2, dist);
  gl_FragColor = vec4(color.rgb * vignette, 1.0);
}
```

```typescript
// 매 movement 의 detect 후 strength 의 ramp
const speed = velocity.length();
material.uniforms.u_vignetteStrength.value = THREE.MathUtils.clamp(speed / 5, 0, 0.6);
```

### 매 Snap turn 의 implementation
```typescript
let lastTurnTime = 0;
const SNAP_ANGLE = Math.PI / 6;  // 30°
const COOLDOWN = 250;

function update(controller: THREE.Group, input: GamepadAxes) {
  const now = performance.now();
  if (Math.abs(input.thumbstickX) > 0.7 && now - lastTurnTime > COOLDOWN) {
    rig.rotation.y -= Math.sign(input.thumbstickX) * SNAP_ANGLE;
    lastTurnTime = now;
    fadeOutIn(50);  // 매 brief blackout 의 ease
  }
}
```

### 매 Teleport locomotion
```typescript
function teleport(targetPos: THREE.Vector3) {
  fadeToBlack(150).then(() => {
    rig.position.copy(targetPos);
    fadeFromBlack(150);
  });
}
```

### 매 Stable horizon (cockpit reference)
```typescript
// 매 vehicle simulator 의 cockpit mesh 의 always-visible
const cockpit = new THREE.Mesh(cockpitGeo, cockpitMat);
camera.add(cockpit);  // 매 head 에 follow — 매 vestibular reference
scene.add(camera);
```

### 매 Comfort 설정 의 노출
```typescript
const settings = {
  movementType: 'teleport' as 'teleport' | 'smooth',
  vignetteEnabled: true,
  snapTurn: true,
  snapAngle: 30,
};
// 매 in-game menu 의 user 노출 — 매 individual variance 의 대응
```

### 매 Latency 의 측정
```typescript
const xrSession = renderer.xr.getSession();
xrSession?.requestAnimationFrame((time, frame) => {
  // 매 frame.predictedDisplayTime - performance.now() = motion-to-photon
});
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| Casual user | teleport + snap turn (default) |
| Hardcore VR | smooth + comfort toggle |
| Vehicle sim | cockpit + stable horizon |
| Stationary game | minimal locomotion (Beat Saber) |
| Motion ride | physical motion 의 sync |

**기본값**: teleport + snap turn 의 default. 매 user toggle 의 expose.

## 🔗 Graph
- 부모: [[Virtual Reality UX]]
- 변형: [[VR 멀미 (VR Sickness)]] · [[VR 멀미(VR sickness)]] (Korean variants)
- 응용: [[Beat Saber]] · [[가상현실(VR) 자전거 시뮬레이터]]
- Adjacent: [[Vergence-Accommodation Conflicts]] · [[깊이 지각(Depth perception)]] · [[Edge Bleeding]]

## 🤖 LLM 활용
**언제**: VR app 의 design 의 comfort 권장, frame rate budget 의 explain, snap turn / teleport 의 trade-off.
**언제 X**: 매 medical 진단 — 매 individual variance 의 큼 의 인지.

## ❌ 안티패턴
- **<72fps 의 ship**: 매 sickness 의 garantee.
- **Smooth-only locomotion**: 매 casual user 의 alienate.
- **Forced camera shake**: 매 vection 증폭.
- **Unstable horizon**: 매 vehicle 의 wobble.

## 🧪 검증 / 중복
- Verified (Oculus VR Best Practices, Valve Half-Life Alyx postmortem).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — VR sickness FULL 작성 |
