💬

React+TypeScriptなWebアプリで、R3Fのtutorial12。(Materials)

2023/12/24に公開

Abstract

今回の参考はここ(Materials)
meshの下記4つを、全部実装してみた。

  • MeshBasicMaterial
  • MeshNormalMaterial
  • MeshPhongMaterial
  • MeshStandardMaterial

結論

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

前提

手順

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

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

フォルダリネームとか
$ BaseProject=react-r3f-tutorial010
$ NewProject=react-r3f-tutorial012
$ 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
$ npm install --save-dev leva

App.tsx

まず全体。

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

+type BoxProps = {
+  props: MeshProps;
+  wireframe?: boolean;
+}

-const Box = (props: MeshProps) => {
+const Box = (boxprops: BoxProps) => {
  const ref = useRef<THREE.Mesh>(null!)

  useFrame((_, delta) => {
    ref.current.rotation!.x += 1 * delta
    ref.current.rotation!.y += 0.5 * delta
  })

+  useControls(boxprops.props.name!, {
+    wireframe: {
+      value: false,
+      onChange: (v: boolean) => {
+        if(boxprops.props.name == "meshBasicMaterial")
+          (ref.current.material as THREE.MeshBasicMaterial).wireframe = v
+        else if(boxprops.props.name == "meshNormalMaterial")
+          (ref.current.material as THREE.MeshNormalMaterial).wireframe = v
+        else if(boxprops.props.name == "meshPhongMaterial")
+          (ref.current.material as THREE.MeshPhongMaterial).wireframe = v
+        else if(boxprops.props.name == "meshStandardMaterial")
+          (ref.current.material as THREE.MeshStandardMaterial).wireframe = v
+      }
+    },
+    flatShading: {
+      value: true,
+      onChange: (v: boolean) => {
+        if(boxprops.props.name == "meshBasicMaterial")
+          {/* (ref.current.material as THREE.MeshBasicMaterial)!.flatShading  = v*/}
+        else if(boxprops.props.name == "meshNormalMaterial")
+          (ref.current.material as THREE.MeshNormalMaterial)!.flatShading  = v;
+        else if(boxprops.props.name == "meshPhongMaterial")
+          (ref.current.material as THREE.MeshPhongMaterial)!.flatShading  = v;
+        else if(boxprops.props.name == "meshStandardMaterial")
+          (ref.current.material as THREE.MeshStandardMaterial)!.flatShading = v;
+        (ref.current.material as THREE.MeshBasicMaterial)!.needsUpdate  = true;
+      },
+    },
+    color: {
+      value: 'lime',
+      onChange: (v: string) => {
+        if(boxprops.props.name == "meshBasicMaterial")
+          (ref.current.material as THREE.MeshBasicMaterial)!.color = new THREE.Color(v)
+        else if(boxprops.props.name == "meshNormalMaterial")
+          {/*(ref.current.material as THREE.MeshNormalMaterial)!.color = new THREE.Color(v)*/}
+        else if(boxprops.props.name == "meshPhongMaterial")
+          (ref.current.material as THREE.MeshPhongMaterial)!.color = new THREE.Color(v)
+        else if(boxprops.props.name == "meshStandardMaterial")
+          (ref.current.material as THREE.MeshStandardMaterial)!.color = new THREE.Color(v)
+      },
+    },
+  })

  return (
-   <mesh {...props} ref={ref}>
+   <mesh {...boxprops.props} ref={ref}>
-     <boxGeometry />
+     <icosahedronGeometry args={[1, 1]} />
    </mesh>
  )
}

const App = () => {
  return (
    <div style={{ width: "75vw", height: "75vh" }}>
      <Canvas camera={{ position: [3, 1, 2] }}>
        <ambientLight />
        <directionalLight />
-       <Box position={[-0.75, 0, 0]} name="A" />
-       <Box position={[ 0.75, 0, 0]} name="B" />
-       <Box position={[-0.75, 2, 0]} name="C" />
-       <Box position={[ 0.75, 2, 0]} name="D" />
+       <Box props={{position:[-1, 0, 0], name:"meshBasicMaterial",    material: new THREE.MeshBasicMaterial()}}/>
+       <Box props={{position:[ 1, 0, 0], name:"meshNormalMaterial",   material: new THREE.MeshNormalMaterial()}}/>
+       <Box props={{position:[-1, 2, 0], name:"meshPhongMaterial",    material: new THREE.MeshPhongMaterial()}}/>
+       <Box props={{position:[ 1, 2, 0], name:"meshStandardMaterial", material: new THREE.MeshStandardMaterial()}}/>
        <OrbitControls />
        <axesHelper args={[5]} />
        <gridHelper />
        <Stats />
      </Canvas>
    </div>
  );
}

export default App;

で、実行。

出来た。

まとめ

特段、理解のムズいとこはなかったけど、TypeScriptに置き換えると、ホントにビルドが通らんかったのが苦しかった。

type BoxPropsを作成した

元のHPでは、wireframeプロパティを普通に使ってるけど、TypeScriptだと通らん。
BoxPropsを作って、MeshPropsをラップした。

useControls::onChangeの引数には型指定が必要。

TypeScriptだと当然なんやけど、気づいてなかった。ハマった~。

materialには、wireframe、flatShading、colorプロパティがない。

まさか、materialに上記のプロパティがないって思わなくって、原因判明に丸一日費やした。
結果的は、個別にキャストしたんだけど、もう少し、スマートな実装があると思う。

ムズかった~。


React+TypeScriptなWebアプリで、R3Fのtutorial11。(パラメータを設定できるやつ。Leva)


React+TypeScriptなWebアプリで、R3Fのtutorial13。(光源)

Discussion