[ReactThreeFiber] 3D 모델 / 애니메이션
3D 모델
가장 많이 사용되는 형식은 3DS, FBX, glTF, OBJ 등 이고, 특히 glTF는 Khronos Group에서 정의한 표준으로 효율성이 아주 뛰어난 형식이라고 한다.
위 사이트에서 3D모델과 애니메이션을 다운로드 받을 수 있다.
모델파일과 애니메이션파일을 블렌더라는 프로그램을 통해 하나의 glTF파일로 생성할 수 있다.
나는 제공하는 예시를 가지고 진행
app.js
<Canvas
shadows
camera={{
near: 1,
far: 100,
position: [7, 7, 0],
}}
>
<ThreeD />
</Canvas>
ThreeD.jsx
function ThreeD() {
const model = useGLTF("./models/model.glb");
return (
<>
<OrbitControls />
<Environment preset="city" />
<primitive scale={5} object={model.scene} />
</>
);
}
export default ThreeD;
3D 모델을 useGLTF("./models/model.glb");
을 통해서 가져오고
<primitive object={model.scene} />
을 통해서 화면에 추가한다.
또한 화면에 추가했으나 광원이 없어 보이지 않기 때문에 Environment을 추가한다.
Environment 의 preset 속성을 이용하면 기본적으로 제공되는 광원을 사용할 수 있다.
이 모델의 중심이 발끝에 맞춰져있다. 따라서 Y축에 대해서 모델의 높이값의 절만만큼 아래로 이동하면 된다.
이를 위해 모델의 높이를 구하려면
useEffect(() => {
let minY = Infinity,
maxY = -Infinity;
model.scene.traverse((item) => {
if (item.isMesh) {
const geomBbox = item.geometry.boundingBox;
if (minY > geomBbox.min.y) minY = geomBbox.min.y;
if (maxY < geomBbox.max.y) maxY = geomBbox.max.y;
}
});
const h = maxY - minY;
console.log(h); // 1.8047408568527317
setHeight(h);
}, [model.scene]);
let minY = Infinity, maxY = -Infinity;
- y축에 대한 최소값과 최대값을 얻기위한 변수 정의
model.scene.traverse()
- 구성 객체를 하나씩 순회한다.
traverse( callback ) {
callback( this );
const children = this.children;
for ( let i = 0, l = children.length; i < l; i++ ) {
children[ i ].traverse( callback );
}
}
model.scene.traverse((item) => {
if (item.isMesh) {
const geomBbox = item.geometry.boundingBox;
if (minY > geomBbox.min.y) minY = geomBbox.min.y;
if (maxY < geomBbox.max.y) maxY = geomBbox.max.y;
}
});
여기서 item은 하나의 객체이며, 아이템이 메쉬일 경우(if (item.isMesh)
) 최소경계 상자(item.geometry.boundingBox
)를 구한다.
경계 상자의 y축에 대한 값을 얻어오는데, 결국 minY, maxY에는 Y축의 최소값과 최대값이 저장된다.
Infinity는 0을 넣은 것과 동일하다.
이후 useState로 height값을 저장하고 position-y={-(height / 2) * 5}
추가. 5를 곱한 이유는 scale이 5이기 때문
애니메이션
애니메이션 정보를 얻기 위해 useAnimation 사용
const animations = useAnimations(model.animations, model.scene)
재생할 수 있는 애니메이션을 UI로 표현하자
const { actionName } = useControls({
actionName: {
value: animations.names[1],
options: animations.names,
},
});
애니메이션을 재생하려면
useEffect(() => {
const action = animations.actions[actionName];
action.play();
}, [actionName]);
하지만 다른 애니메이션을 클릭하면 이전 상태의 애니메이션도 동시에 적용되고 있는 상태이기 때문에, 이전 상태를 지우고 현재상태를 유지해야한다.
action.reset().fadeIn(0.5).play();
return () => {
action.fadeOut(0.5);
};
reset() - 새롭게 선택된 애니메이션을 첫번째 프레임으로 갖는다.
fadeIn(0.5) - 0.5초에 걸쳐서 서서의 애니메이션이 재생될 수 있도록
return () => {action.fadeOut(0.5)}
이전의 애니메이션을 0.5초에 걸쳐서 서서히 사라지도록한다.
댓글남기기