다이렉트 터치

Direct touch

손을 뻗어 손끝으로 허공의 버튼을 실제로 "누르는" 동작 — 핀치와 달리 손이 콘텐츠와 같은 위치에 있어야 한다.

다른 이름: Direct interactionPoke
···
js
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(48, 1, 0.1, 20);
camera.position.set(0, 0.2, 4.4);
camera.lookAt(0, 0, 0);
scene.add(new THREE.HemisphereLight(0xbcd0ff, 0x101018, 1.5));
const key = new THREE.DirectionalLight(0xffffff, 1.1); key.position.set(2, 3, 3); scene.add(key);
const btn = new THREE.Mesh(new THREE.CylinderGeometry(0.7, 0.7, 0.22, 40), new THREE.MeshStandardMaterial({ color: 0x5b5bf7, roughness: 0.4 }));
btn.rotation.x = Math.PI / 2;
scene.add(btn);
const tip = new THREE.Mesh(new THREE.SphereGeometry(0.14, 24, 24), new THREE.MeshStandardMaterial({ color: 0xffd7a8 }));
scene.add(tip);
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
const BASE_Z = 0.11, PRESS_Z = -0.05;
renderer.setAnimationLoop((t) => {
  const p = (Math.sin(t * 0.0013) + 1) / 2;
  const finZ = 2.4 - p * 2.28;
  tip.position.set(0, 0, finZ);
  const pressed = finZ < BASE_Z + 0.05;
  btn.position.z += ((pressed ? PRESS_Z : BASE_Z) - btn.position.z) * 0.35;
  btn.material.color.set(pressed ? 0x8a8aff : 0x5b5bf7);
  renderer.render(scene, camera);
});

핸드 트래킹 핀치가 "먼 곳을 시선으로 고르고 손은 편하게" 라면, 다이렉트 터치는 반대다. 버튼이 팔이 닿는 거리(컴포트 존) 안에 있을 때, 사용자가 손가락을 실제로 그 위치까지 뻗어 표면을 눌러서 확정한다. 스마트폰 화면을 누르는 감각과 가장 가깝다.

이 방식은 손가락 끝이 가상 표면에 닿는 깊이(터치 임계값)를 인식해서, 버튼이 실제로 눌리는 시각·촉각적(visionOS는 미세한 오디오/비주얼 피드백으로 대체) 피드백을 준다. 핀치보다 학습이 필요 없고 직관적이지만, 팔을 계속 뻗고 있어야 해서 오래 쓰면 피로하다("고릴라 암" 문제).

데모는 손끝이 앞으로 다가와 버튼 표면에 닿는 순간 버튼이 눌리는 걸 보여준다 — 핀치 데모와 달리 손가락이 버튼과 같은 자리까지 이동한다는 점을 눈여겨보세요.

언제 쓰나

손 닿는 거리 안의 근접 UI(작은 패널, 키보드)에 씁니다. 팔을 계속 들고 있어야 하는 배치는 피로를 유발하니 오래 쓰는 컨트롤에는 핀치를 고려하세요.