React-Three-Fiber Dynamic points

김찬진·2024년 1월 12일
1

Three

목록 보기
6/8

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 컴포넌트를 담는 캔버스를 정의합니다. 여기서 중요한점은 PointsNodeMaterialWebGPURenderer 일경우에만 작동합니다. 그에따라 기본으로 정의된 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} />;
};

전체 코드
모델은 달리는 애니메이션이 지원되는 모델을 사용했습니다.

useLoaderGLTFLoader 를 사용하여 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를 불러옵니다.

완성

profile
AI/Web 개발자

0개의 댓글