Three.JS의 PointsNodeMaterial
이 나오면서 기존 Glb,GLTF 모델을 point cloud로 구현하는게 가능해졌습니다. Three.JS의 코드를 R3F에 적용해 보겠습니다.
React three fiber 및 Three.JS를 어느정도 알고 계신다는 가정하에 코드를 작성합니다.
const Scene = () => {
const aspect_ratio = window.innerWidth / window.innerHeight;
const cameraRef = useRef();
const cameraHandler = () => {
// console.log(cameraRef.current);
};
const pervPosition = new THREE.Vector3(1.03, 0.95, 1.1);
return (
<>
<directionalLight intensity={5} />
<OrbitControls enableRotate={true} onChange={cameraHandler} />
<PerspectiveCamera
makeDefault
aspect={aspect_ratio}
castShadow={true}
far={1000}
focus={10}
fov={75}
near={0.1}
position={pervPosition}
rotation={[-0.71, 0.62, 0.46]}
ref={cameraRef}
/>
<Light />
<GltfModel />
</>
);
};
Scene
컴포넌트를 정의합니다. cameraHandler 함수는 OrbitContols
로 카메라를 조작하여 그 카메라의 vector3 값을 측정하기 위해 사용합니다. 빛은 ambientLight
또는 directionalLight
아무거나 사용합니다.
const ThreeContainer = () => {
return (
<div className="scene_container">
<Canvas style={{ width: '100vw', height: '100vh' }} gl={(canvas) => new WebGPURenderer({ canvas })}>
<Scene />
</Canvas>
</div>
);
};
Scene
컴포넌트를 담는 캔버스를 정의합니다. 여기서 중요한점은 PointsNodeMaterial
은 WebGPURenderer 일경우에만 작동합니다. 그에따라 기본으로 정의된 WebGLRenderer
를 변경해줍니다.
WebGPURenderer 는 아래의 코드로 import 할 수 있습니다.
import WebGPURenderer from 'three/examples/jsm/renderers/webgpu/WebGPURenderer';
import { uniform, skinning, PointsNodeMaterial } from 'three/examples/jsm/nodes/Nodes.js';
필요한 모듈을 import 합니다.
const GltfModel = () => {
let scene, mixer;
const { position, rotation, scale } = useControls('webgpu gltf point model', {
position: [0, -1.38, 0],
rotation: [1, 0.28, 0],
scale: 0.015,
});
const gltf = useLoader(GLTFLoader, '/models/FastRun.glb');
mixer = new THREE.AnimationMixer(gltf.scene);
const action = mixer.clipAction(gltf.animations[0]);
scene = new THREE.Scene();
action.play();
gltf.scene.traverse((mesh) => {
if (mesh.isMesh) {
mesh.visible = false;
const materialPoints = new PointsNodeMaterial();
materialPoints.colorNode = uniform(new THREE.Color());
materialPoints.positionNode = skinning(mesh);
const pointCloud = new THREE.Points(mesh.geometry, materialPoints);
scene.add(pointCloud);
console.log(mesh.geometry);
}
});
scene.add(gltf.scene);
useFrame((state, delta) => {
mixer.update(delta);
});
return <primitive object={scene} scale={scale} position={position} rotation={rotation} />;
};
전체 코드
모델은 달리는 애니메이션이 지원되는 모델을 사용했습니다.
useLoader
및 GLTFLoader
를 사용하여 GLTF,GLB 모델을 로드합니다.
AnimationMixer
를 통해 모델의 애니메이션을 재생합니다.
여기에서 기존의 React Three Fiber는 geometry와 material은 return문 안에 컴포넌트 형식으로 생성하지만 PointsNodeMaterial
Three.JS 기반이기때문에 직접 Scene을 생성하고 거기에 넣어줘야합니다.
scene = new THREE.Scene();
scene.add(gltf.scene);
traverse
함수를 통해 mesh의 재질을 point cloud로 변경해줍니다. 여기에서 위에서 import 하던
uniform, skinning, PointsNodeMaterial 가 사용됩니다.
gltf.scene.traverse((mesh) => {
if (mesh.isMesh) {
mesh.visible = false;
const materialPoints = new PointsNodeMaterial();
materialPoints.colorNode = uniform(new THREE.Color());
materialPoints.positionNode = skinning(mesh);
const pointCloud = new THREE.Points(mesh.geometry, materialPoints);
scene.add(pointCloud);
console.log(mesh.geometry);
}
});
여기에서 mesh.visible = false;
를 통해 기존 모델을 비활성화하는 이유는, pointNodeMaterial로 생성된 Scene은 기존 GLTF,GLB 모델과 별개로 생성되기때문입니다.
useFrame((state, delta) => {
mixer.update(delta);
});
useFrame
을 사용하여 GLTF,GLB 모델의 애니메이션을 실시간으로 업데이트해줍니다.
return <primitive object={scene} scale={scale} position={position} rotation={rotation} />;
Scene으로 point cloud mesh를 추가했기때문에 기존의 방식과는 다르게 GLTF,GLB 모델을 불러오는 것과 같은 방식으로 primitive
를 사용하여 mesh를 불러옵니다.
완성