[Electron] Vite+Electron+React+Three.js 2 (静的アセット・シェーダーファイルの読み込み)

2023-05-15

2023-05-15

この記事は[Electron] Vite+Electron+React+Three.js 1 (環境構築)の続きです。

Vite+Electron+React+Three.jsで、アセット(画像ファイル、3Dモデル)やシェーダーファイルを読み込む方法を解説します。

electron_1

サンプルプロジェクト

https://github.com/yasuhirohoshino/Electron_Examples/tree/main/Electron_R3F

実行環境

Windows 10 Pro 22H2

静的アセットの配置・読み込み

src/rendererディレクトリ以下に、publicディレクトリを作成し、アセットファイルを配置します。

dir_1

publicディレクトリ以下のファイルは、./からの相対パスで読み込むことができます。

...
import { useTexture } from '@react-three/drei'

function Img() {
  const colorMap = useTexture('./images/img_1.jpg')
  return (
    <mesh>
      <planeGeometry args={[2, 2]} />
      <meshBasicMaterial map={colorMap} side={THREE.DoubleSide} />
    </mesh>
  )
}
...

glTFやFBXなども、同様に読み込めます。

import { AnimationMixer, Mesh } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'

function FBXModel() {
  const obj = useLoader(FBXLoader, './models/man_walking.fbx')
  const [mixer] = useState(() => new AnimationMixer(obj))
  const action = mixer.clipAction(obj.animations[0])
  action.play()

  obj.traverse((child) => {
    if (child instanceof Mesh) {
      child.castShadow = true
      child.receiveShadow = true
    }
  })

  useFrame((_state, delta) => {
    mixer.update(delta)
  })

  return (
    <mesh>
      <primitive object={obj} />
    </mesh>
  )
}

function GLTFModel() {
  const gltf = useLoader(GLTFLoader, './models/gltf/alarm_clock_01_1k.gltf')
  gltf.scene.traverse((child) => {
    child.castShadow = true
    child.receiveShadow = true
  })

  return (
    <mesh>
      <primitive object={gltf.scene} />
    </mesh>
  )
}

ただし、glbファイルを読み込む場合は、エラーが発生するため、HTML内のmetaタグで、Content-Security-Policyconnect-srcblob:を追加する必要があります。

<meta
  http-equiv="Content-Security-Policy"
  content="worker-src blob: ; connect-src 'self' https://market-assets.fra1.cdn.digitaloceanspaces.com/ https://fonts.gstatic.com/ blob:; default-src 'self'; script-src 'self' blob:; style-src 'self' 'unsafe-inline'"
/>

シェーダーファイルの配置・読み込み

src/renderer/src以下に任意のディレクトリを作成し、シェーダーファイルを配置します。

dir_2

また、以下のような.d.tsファイルを作成し、src/renderer/src以下に配置します。

declare module '*.vert' {
  const value: string
  export default value
}

declare module '*.frag' {
  const value: string
  export default value
}

declare module '*.glsl' {
  const value: string
  export default value
}

シェーダーのコード内で#pragma includeを用い別のシェーダーファイルを読み込みたい場合、npm i -D vite-plugin-glslでvite-plugin-glslをインストールし、electron.vite.config.tsに以下の設定を追加します。

...
import glsl from 'vite-plugin-glsl';

export default defineConfig({
  ...
  renderer: {
    ...
    plugins: [react(), glsl()],
  }
})

以下はRenderer内でシェーダーを読み込んだ際のサンプルコードです。

import testShader_vert from './shaders/test.vert'
import testShader_frag from './shaders/test.frag'

function ShaderMesh(props) {
  const matRef = useRef<THREE.ShaderMaterial>(null!)

  useFrame((state) => {
    const { clock } = state
    matRef.current.uniforms.time = { value: clock.getElapsedTime() * 0.1 }
  })

  return (
    <group {...props}>
      <mesh position={[0.0, 1.0, 0.0]}>
        <planeGeometry args={[2, 2]} />
        <shaderMaterial
          ref={matRef}
          vertexShader={testShader_vert}
          fragmentShader={testShader_frag}
          side={THREE.DoubleSide}
          alphaTest={0.5}
        />
      </mesh>
      <Caption>GLSL</Caption>
    </group>
  )
}