🙆‍♀️

React+TypeScriptなWebアプリで、R3Fのtutorial16。(GLTFloaderで3Dモデル表示)

2023/12/27に公開


https://aaaa1597.github.io/react-r3f-tutorial016/

Abstract

やっと、ここまできた。3Dの勉強するなら3Dモデルを表示したいよね。
ってことで、ここのトトロを表示させてみた。
今回の参考はここ(GLTFloader)
GLTF形式のモデルを表示する。FBXとかobjとかmqoとかだったら馴染みがあるけど、GLTF形式って知らんかった。OpenGL企画した団体と同じところが企画したみたいやけん、すたれる心配はなさそう。

結論

今回の成果物はココ↓
https://github.com/aaaa1597/react-r3f-tutorial016

GLTFloader使い方

とはいっても、GLTFloaderを直接使うわけでなく、useLoader()の第一引数に渡すだけ。あとは、R3Fがいい感じにやってくれる。

前提

手順

1.プロジェクト生成 -> VSCodeで開く

めんどいから、このスケルトンコードから始める。react-r3f-tutorial015
で、下記コマンドでフォルダ名とか整備する。

フォルダリネームとか
$ BaseProject=react-r3f-tutorial015
$ NewProject=react-r3f-tutorial016
$ cd ~
$ rm -rf ${BaseProject}
$ git clone https://github.com/aaaa1597/${BaseProject}.git
$ rm -rf ${BaseProject}/.git
$ mv ${BaseProject} ${NewProject}
$ cd ${NewProject}

準備

$ npm install --save three
$ npm install --save @types/three
$ npm install --save @react-three/fiber
$ npm install --save @react-three/drei
$ mkdir -p public/models
$ wget https://github.com/aaaa1597/react-r3f-tutorial016/blob/main/public/models/Totoro.glb public/models

.eslintrc.js

ビルドエラー回避の設定

.eslintrc.js
     "rules": {
-        "react/no-unknown-property": ['error', { ignore: ['css', "args", 'rotation-x', "castShadow", 'receiveShadow'] }],
+        "react/no-unknown-property": ['error', { ignore: ['css', "args", 'rotation-x', "castShadow", 'receiveShadow', 'position', 'intensity', 'object', 'children-0-castShadow'] }],
     }

App.tsx

まず全体。

App.tsx
-import React, {useRef} from 'react';
+import React from 'react';
import './App.css';
-import { Canvas, useFrame, MeshProps, useLoader } from '@react-three/fiber'
+import { Canvas, useLoader } from '@react-three/fiber'
-import * as THREE from 'three'
import { Stats, OrbitControls } from '@react-three/drei'
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

-const Lights = () => {
-  const directionalRef = useRef<THREE.DirectionalLight>(null!)
-
-  useControls('Directional Light', {
-    intensity: {
-      value: 1, min: 0, max: 5, step: 0.1,
-      onChange: (v) => {
-        directionalRef.current.intensity = v
-      },
-    },
-    position: {
-      value: [3.3,1.0,4.4],
-      onChange: (v) => {
-        directionalRef.current.position.x = v[0]
-        directionalRef.current.position.y = v[1]
-        directionalRef.current.position.z = v[2]
-      },
-    },
-  })
-  
-  return (
-    <directionalLight ref={directionalRef} castShadow />
-  )
-}
-
-type BoxProps = {
-  props: MeshProps;
-  wireframe?: boolean;
-}
-
-const Box = (boxprops: BoxProps) => {
-  const ref = useRef<THREE.Mesh>(null!)
-
-  useFrame((_, delta) => {
-    if( !ref.current) return
-    ref.current.rotation.x += 1 * delta
-    ref.current.rotation.y += 0.5 * delta
-  })
-
-  return (
-    <mesh {...boxprops.props} ref={ref}>
-      <boxGeometry />
-    </mesh>
-  )
-}

const Floor = () => {
  return (
    <mesh rotation-x={-Math.PI / 2} receiveShadow={true}>
      <circleGeometry args={[10]}/>
      <meshStandardMaterial />
    </mesh>
  )
}

const App = () => {
- const texture = useLoader(THREE.TextureLoader, './imgs/grid.png')
+ const gltf = useLoader(GLTFLoader, 'models/Totoro.glb')

  return (
    <div style={{ width: "75vw", height: "75vh" }}>
      <Canvas camera={{ position: [4, 4, 3] }} shadows>
-        <Lights />
-        <Box props={{position:[3, 1, 0], name:"meshBasicMaterial",    material: new THREE.MeshBasicMaterial({ map: texture })}}/>
-        <Box props={{position:[1, 1, 0], name:"meshNormalMaterial",   material: new THREE.MeshNormalMaterial({flatShading: true,})}}/>
-        <Box props={{position:[1, 3, 0], name:"meshPhongMaterial",    material: new THREE.MeshPhongMaterial({flatShading: true, map: texture,})}}/>
-        <Box props={{position:[3, 3, 0], name:"MeshStandardMaterial", material: new THREE.MeshStandardMaterial({flatShading: true, map: texture,})}}/>
+       <directionalLight position={[3.3, 1.0, 4.4]} intensity={Math.PI * 2} castShadow/>
+       <primitive object={gltf.scene} position={[0, 1, 0]} children-0-castShadow/>
        <Floor />
        <OrbitControls />
        <axesHelper args={[5]} />
        <gridHelper />
        <Stats />
      </Canvas>
    </div>
  );
}

export default App;

で、実行。


出来た!!

2FPSってところが今後の課題だな。
web版なら60fps出るからいいや。


React+TypeScriptなWebアプリで、R3Fのtutorial15。(useLoaderでテクスチャ読込み)


React+TypeScriptなWebアプリで、R3Fのtutorial17。(Environmentで簡単背景描画)

Discussion