💬

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

2023/12/24に公開

Abstract

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

  • ambientLight ... 環境光源
  • directionalLight ... 指向性光源(太陽光)
  • pointLight ... 点光源
  • spotLight ... 線光源

結論

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

前提

手順

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

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

フォルダリネームとか
$ BaseProject=react-r3f-tutorial012
$ NewProject=react-r3f-tutorial013
$ 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 = (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)
-     },
-   },
- })

+const Lights = () => {
+  const ambientRef = useRef<THREE.AmbientLight>(null!)
+  const directionalRef = useRef<THREE.DirectionalLight>(null!)
+  const pointRef = useRef<THREE.PointLight>(null!)
+  const spotRef = useRef<THREE.SpotLight>(null!)
+  
+  useControls('Ambient Light', {
+    visible: {
+      onChange: (v) => {
+        ambientRef.current.visible = v
+      },
+    color: {
+      value: 'white',
+      onChange: (v) => {
+        ambientRef.current.color = new THREE.Color(v)
+      },
+    },
+  });
+
+  useControls('Directional Light', {
+    visible: {
+        directionalRef.current.visible = v
+      },
+    },
+    position: {
+      value: [1,1,1],
+      onChange: (v) => {
+        directionalRef.current.position.x = v[0]
+        directionalRef.current.position.y = v[1]
+        directionalRef.current.position.z = v[2]
+      value: 'white',
+        directionalRef.current.color = new THREE.Color(v)
+  });
+
+  useControls('Point Light', {
+    visible: {
+      value: false,
+      onChange: (v) => {
+        pointRef.current.visible = v
+      },
+    },
+    position: {
+      value: [3,0,0],
+      onChange: (v) => {
+        pointRef.current.position.x = v[0]
+        pointRef.current.position.y = v[1]
+        pointRef.current.position.z = v[2]
+      },
+    },
+    color: {
+      value: 'white',
+      onChange: (v) => {
+        pointRef.current.color = new THREE.Color(v)
+      },
+    },
+  })
+  
+  useControls('Spot Light', {
+    visible: {
+      value: false,
+      onChange: (v) => {
+        spotRef.current.visible = v
+      },
+    },
+    position: {
+      value: [3,3,3],
+      onChange: (v) => {
+        spotRef.current.position.x = v[0]
+        spotRef.current.position.x = v[1]
+        spotRef.current.position.x = v[2]
+      },
+    },
+    color: {
+      value: 'white',
+      onChange: (v) => {
+        spotRef.current.color = new THREE.Color(v)
+      },
+    },
+  })

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

const Lights = () => {
  const ambientRef = useRef<THREE.AmbientLight>(null!)
  const directionalRef = useRef<THREE.DirectionalLight>(null!)
  const pointRef = useRef<THREE.PointLight>(null!)
  const spotRef = useRef<THREE.SpotLight>(null!)
  
  useControls('Ambient Light', {
    visible: {
      value: false,
      onChange: (v) => {
        ambientRef.current.visible = v
      },
    },
    color: {
      value: 'white',
      onChange: (v) => {
        ambientRef.current.color = new THREE.Color(v)
      },
    },
  });

  useControls('Directional Light', {
    visible: {
      value: true,
      onChange: (v: boolean) => {
        directionalRef.current.visible = v
      },
    },
    position: {
      value: [1,1,1],
      onChange: (v) => {
        directionalRef.current.position.x = v[0]
        directionalRef.current.position.y = v[1]
        directionalRef.current.position.z = v[2]
      },
    },
    color: {
      value: 'white',
      onChange: (v: string) => {
        directionalRef.current.color = new THREE.Color(v)
      },
    },
  });

  useControls('Point Light', {
    visible: {
      value: false,
      onChange: (v) => {
        pointRef.current.visible = v
      },
    },
    position: {
      value: [3,0,0],
      onChange: (v) => {
        pointRef.current.position.x = v[0]
        pointRef.current.position.y = v[1]
        pointRef.current.position.z = v[2]
      },
    },
    color: {
      value: 'white',
      onChange: (v) => {
        pointRef.current.color = new THREE.Color(v)
      },
    },
  })
  
  useControls('Spot Light', {
    visible: {
      value: false,
      onChange: (v) => {
        spotRef.current.visible = v
      },
    },
    position: {
      value: [3,3,3],
      onChange: (v) => {
        spotRef.current.position.x = v[0]
        spotRef.current.position.x = v[1]
        spotRef.current.position.x = v[2]
      },
    },
    color: {
      value: 'white',
      onChange: (v) => {
        spotRef.current.color = new THREE.Color(v)
      },
    },
  })
  
  return (
    <>
      <ambientLight ref={ambientRef} />
      <directionalLight ref={directionalRef} />
      <pointLight ref={pointRef} />
      <spotLight ref={spotRef} />
    </>
  )
}

const App = () => {
  return (
    <div style={{ width: "75vw", height: "75vh" }}>
      <Canvas camera={{ position: [3, 1, 2] }}>
+       <Lights />
        <Box props={{position:[-1, 0, 0], name:"meshBasicMaterial",    material: new THREE.MeshBasicMaterial({ color: 'yellow'})}}/>
        <Box props={{position:[ 1, 0, 0], name:"meshNormalMaterial",   material: new THREE.MeshNormalMaterial({ flatShading: true })}}/>
        <Box props={{position:[-1, 2, 0], name:"meshPhongMaterial",    material: new THREE.MeshPhongMaterial({ color: 'lime', flatShading: true })}}/>
        <Box props={{position:[ 1, 2, 0], name:"meshStandardMaterial", material: new THREE.MeshStandardMaterial({ color: 0xff0033, flatShading: true })}}/>
-       <OrbitControls />
        <OrbitControls target={[2, 2, 0]} />
        <axesHelper args={[5]} />
        <gridHelper />
        <Stats />
      </Canvas>
    </div>
  );
}

export default App;

で、実行


出来た!!

まとめ

変更点は、すごい量だけど、実際には、ambientLight(環境光源)、directionalLight(指向性光源(太陽光))、pointLight(点光源)、spotLight(線光源)の4行が重要なところ。

ambientLight(環境光源)

要は、<ambientLight/> を設定するだけ。

directionalLight(指向性光源)

<directionalLight/> を設定するだけ。

pointLight(点光源)

<pointLight/> を設定するだけ。

spotLight(線光源)

<spotLight/> を設定するだけ。

また、TypeScriptにしたことで、ビルドが通らんのには苦しんだ。


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


React+TypeScriptなWebアプリで、R3Fのtutorial14。(影(Shadow))

Discussion