일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- REST API
- uint8array
- FirebaseAnalytics
- methodChannel
- RouteObserver
- webrtc
- Completer
- react
- Babel standalone
- Flutter
- Three-fiber
- jszip
- babel
- Image Resize typescript
- Three js
- userevent_tracker
- Raycasting
- web track
- Prism.js
- uint16array
- swagger-typescript-api
- androidId
- Excel
- Redux
- typescript
- Game js
- identifierForVender
- KakaoMap
- code editor
- node
- Today
- Total
Never give up
Three js - mouse event(feat. Raycaster) 본문
이번에는 mesh에 이벤트를 넣어주는 작업을 해봤는데
html에서 사용하던 onClick이나 mouseEnter 같은 이벤트를 따로 걸어줄 수 없어서
eventListener를 걸어서 사용했고, 3d에서는 조금 더 복잡한 방법으로 사물의 위치를 추적해야되는거 같습니다
그래서 raycaster를 사용할텐데 자세한 내용은 아래 링크를 참고해주세요
(링크 : https://threejs.org/docs/#api/en/core/Raycaster)
화면부분은 간단한 div에 ref만 할당하는 부분으로 동일합니다
use_raycasting.ts
import { useEffect, useRef } from "react";
import * as Three from "three";
const useRaycasting = () => {
const ref = useRef<HTMLDivElement>(null);
const width = window.innerWidth;
const height = window.innerHeight;
const scene = new Three.Scene();
let camera: Three.PerspectiveCamera;
let light: Three.AmbientLight;
let renderer: Three.WebGLRenderer;
const raycaster = new Three.Raycaster();
const mousePosition = new Three.Vector2();
let intersects: Three.Intersection<Three.Object3D<Three.Event>>[] = [];
let debounce: number | undefined;
let hovered: any = null;
useEffect(() => {
window.addEventListener('mousemove', mouseMoveListener);
window.addEventListener('click', clickListener);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('mousemove', mouseMoveListener);
window.removeEventListener('click', clickListener);
window.removeEventListener('resize', handleResize);
};
}, []);
useEffect(() => {
if (ref.current !== null) {
setRenderer();
ref.current.appendChild(renderer.domElement);
setCamaera();
setLight();
setMeshes();
animate();
}
}, [ref]);
const setRenderer = () => {
renderer = new Three.WebGLRenderer();
renderer.setSize(width, height);
};
const setCamaera = () => {
camera = new Three.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.z = 5;
};
const setLight = () => {
light = new Three.AmbientLight(0xffffff, 5);
scene.add(light);
};
const setMeshes = () => {
for (let i = 0; i < 3; i++) {
const cube = makeCube();
cube.position.set(2 - (i * 2), 0, 0);
scene.add(cube);
}
};
const makeCube = () => {
const geometry = new Three.BoxGeometry(1, 1, 1);
const material = new Three.MeshMatcapMaterial({ color: 0x00ff00 });
return new Three.Mesh(geometry, material);
};
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
const mouseMoveListener = (e: MouseEvent) => {
clearTimeout(debounce);
debounce = setTimeout(() => {
mousePosition.set((e.clientX / width) * 2 - 1, -(e.clientY / height) * 2 + 1);
raycaster.setFromCamera(mousePosition, camera);
intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
hovered = intersects[0].object as any;
hovered.material.color.set('#229922');
} else {
if (hovered !== null) {
hovered.material.color.set('#00ff00');
}
}
}, 10);
};
const clickListener = (e: MouseEvent) => {
if (intersects.length > 0) {
const selectedCube = intersects[0].object;
const isUpScailed = selectedCube.scale.x === 1.1;
selectedCube.scale.setScalar(isUpScailed ? 1.0 : 1.1);
}
};
const handleResize = () => {
if (ref !== null) {
const width = ref.current!.clientWidth;
const height = ref.current!.clientHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
};
};
return { ref };
};
export default useRaycasting;
useEffect에서 mouse move, click event에 등록해주고
먼저 mouseMove할 때 위치 셋팅하게 되는데
마우스 좌표의 범위가 -1에서 1까지이기 때문에 화면에 위치한 마우스의 좌표값에 따른 계산을 해줘야됩니다
계산이 왜 이렇게 되는지 간단하게 표현해보자면
(만든애들이 이렇게 하라고해서...)
화면을 그림과 같이 2등분 하고, 마우스 위치에 따라 -1에서 1까지 표현을 할 때
마우스의 위치 / 화면의 넓이 * 2 (여기까지가 2등분을 하고) - 1을 해주면
A영역에 있을 때는 음수(최소 -1), B영역에 있을 때는 양수(최대 1) 까지 표현 가능합니다
그 후 raycast에 마우스의 위치와 카메라가 방향을 같이 넣어줍니다
raycasting에 대해 자세히 알고싶으신 분들을 위해 링크 남겨드립니다
(링크 : https://en.wikipedia.org/wiki/Ray_casting)
그 후 raycaster가 포착(?)한 object들을 배열에 넣어주고
클릭할 때 scale 변경, hover일 때 색상 변경을 넣어주는 코드를 만들어봤습니다
hover할 때 성능을 조금 개선해보고자 debounce를 넣어봤는데
나중에 조금 더 거대한(?) 사물에 이벤트를 넣을 때 도움이 되지않을까 하는 헛된 상상을 하고 있습니다
그리고 ts 라이브러리 만드신분이 interface에 따로 material을 안넣어두셔서 any로 캐스팅을 해줬습니다
사실 따로 라이브러리를 사용 안해서 코드가 길어지고 복잡도도 높아지고 있는데
웬만하면 이미 자주 사용하는 기능들이 만들어진 react three fiber같은 좋은 라이브러리가 있으니
필자처럼 삽질안해보셔도 됩니다
필자는 왜 삽질하는지 궁금하신 분들이 있으실텐데
(사실 저도 잘 모르겠습니다 왜 굳이 안해도 되는 노력을 하고 있는지..)
다음에 포스팅 예정중인 3d model부분을 적용해보고 나서
바로 사용해볼 예정입니다
'Three js' 카테고리의 다른 글
Three js - Fiber example (0) | 2023.06.03 |
---|---|
Three js - GLTF example (0) | 2023.05.07 |
Three js - gauge example (0) | 2023.04.08 |
Three js - tutorial (0) | 2023.04.08 |