Files
2nd/10_Wiki/Topics/Programming & Language/VR Sickness.md
T
2026-05-10 22:08:15 +09:00

175 lines
5.4 KiB
Markdown

---
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 작성 |