LoginSignup
2
1

[ReactThreeFiber]Viteで自作Shader環境を整える

Posted at

はじめに

毎回Shader環境を作成するのが面倒だったので、
R3F+Vite+Typescript+GLSLShaderテンプレート作成しました。
今回作成した成果物は以下です。
today-shader01aa.gif

Githubは以下です。

環境構築

Viteでプロジェクト作成

[Project name]» r3f-ts-shader-starter
[Select a framework:] » React
[Select a variant] » TypeScript + SWC

依存関係をインストール

cd r3f-ts-shader-starter
pnpm install
pnpm install three @types/three @react-three/fiber @react-three/drei
pnpm install --save-dev vite-plugin-glsl

Shader環境構築(GLSL)

vite.config.ts
import { defineConfig } from 'vite'
import glsl from "vite-plugin-glsl"
import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), glsl(
    {
      include: [
        '**/*.glsl', '**/*.wgsl',
        '**/*.vert', '**/*.frag',
        '**/*.vs', '**/*.fs'
      ],
      exclude: undefined,          // Glob pattern, or array of glob patterns to ignore
      warnDuplicatedImports: true, // Warn if the same chunk was imported multiple times
      defaultExtension: 'glsl',    // Shader suffix when no extension is specified
      compress: false,             // Compress output shader code
      watch: true,                 // Recompile shader on change
      root: '/'                    // Directory for root imports
    }
  )],
})

起動

pnpm dev

**http://localhost:5173/**で確認しましょう。

実装

WebGLのCanvasを実装

App.tsx
import { Canvas } from "@react-three/fiber";
import { OrbitControls, GizmoHelper, GizmoViewport } from "@react-three/drei";

function App() {
  return (
    <div style={{ height: "100dvh", width: "100dvw" }}>
      <Canvas shadows>
        <ambientLight intensity={1}/>
        <pointLight position={[3, 3, 3]}/>
        <directionalLight position={[-2, 3, 5]}/>
        <mesh>
          <boxGeometry args={[1, 1, 1]}/>
          <meshStandardMaterial color="hotpink"/>
        </mesh>
        <OrbitControls/>
        <GizmoHelper alignment="top-right" margin={[75, 75]}>
          <GizmoViewport labelColor="white" axisHeadScale={1} />
        </GizmoHelper>
      </Canvas>
    </div>
  )
}

export default App

image.png

画面をぐりぐりするとCanvasの視点が動くはずです。

Shaderを実装

srcフォルダ以下にglslフォルダを作成し、
hello.vertとhello.fragを作成

└ src
+  └ glsl
+     ├ hello.frag
+     └ hello.vert

FragmentShaderの記述

テスト用なので、
なんかいい感じのShaderを作成します。

hello.frag
varying vec2 vUv;
uniform float u_time;

/**
 * Line関数
 * @params uv     位置データ
 * @params speed  波の速さ
 * @params height 周波数の高さ
 * @params col    色データ
 */
vec4 Line(vec2 uv, float speed, float height, vec3 col){
  uv.y += smoothstep(1., 0., abs(uv.x)) * sin(u_time * speed + uv.x * height) * .2;
  return vec4(
    smoothstep(
      .06 * smoothstep(.2, .9, abs(uv.x)), 
      0., 
      abs(uv.y) - .004
    ) * col, 
    1.0) * smoothstep(1., .3, abs(uv.x));
}

void main() {
  vec2 uv = vUv;
  uv -= 0.5; // 中心
  uv.x *= 1.6; // [0:1.6]区画にスケール
  vec4 color = vec4(0.);
  float lattice = 5.0;
  for (float i = 0.; i <= lattice; i += 1.) {
    float lattice = 5.0;
    float t = i / lattice; // [0, 0.25, 0.50, 0.75, 1.0]
    color += Line(
      uv, 
      1. + t,
      4. + t, 
      vec3(
        .2 + t * .7, 
        .2 + t * .4, 
        0.3
      )
    );
}

  gl_FragColor = vec4(color);
}

VertexShader作成

Vertexは面倒なので初期値で。

hello.vert
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

R3F側で実装

App.tsx
+ import React from "react";
+ import { Canvas, useFrame } from "@react-three/fiber";
import { OrbitControls, GizmoHelper, GizmoViewport } from "@react-three/drei";
+ import vertexShader from "./glsl/hello.vert";
+ import fragmentShader from "./glsl/hello.frag";
+ import { ShaderMaterial } from "three";

function App() {
  return (
    <div style={{ height: "100dvh", width: "100dvw" }}>
      <Canvas shadows>
        <ambientLight intensity={1}/>
        <pointLight position={[3, 3, 3]}/>
        <directionalLight position={[-2, 3, 5]}/>
-         <mesh>
-           <boxGeometry args={[1, 1, 1]}/>
-           <meshStandardMaterial color="hotpink"/>
-         </mesh>
+         <ShaderPlane/>
        <OrbitControls/>
        <GizmoHelper alignment="top-right" margin={[75, 75]}>
          <GizmoViewport labelColor="white" axisHeadScale={1} />
        </GizmoHelper>
      </Canvas>
    </div>
  )
}

+ const ShaderPlane = () => {
+ 
+   const ref = React.useRef<ShaderMaterial>(null);
+   useFrame((_, delta) => {
+     if (ref.current) {
+       ref.current.uniforms.u_time.value += + delta;
+     }
+   });
+ 
+   return (
+     <mesh scale={3}>
+       <planeGeometry args={[1, 1, 1]}/>
+       <shaderMaterial
+         ref={ref}
+         vertexShader={vertexShader} 
+         fragmentShader={fragmentShader}
+         uniforms={{
+           u_time: { value: 0.0 }
+         }}
+      />
+     </mesh>
+  )
+ };

export default App

[実行結果]

today-shader01aa.gif

おわりに

以前書いたCRAを使ったShader環境構築は微妙なので、これからはこっちでやろうと思ってます。

PS: 本当にReactThreeFiberは楽しいです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1