1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

R3Fで自作Shader環境を整える

Last updated at Posted at 2023-02-13

はじめに

もうCreateReactAppは非推奨なため、以下のVite環境で構築する方がおすすめです。

最近Shaderにさわり始めたのですが、
glslファイルを直接文字列といれるのは面倒くさく感じたので、
GLSLManagerというクラスをuseContextで使えるようにしました。

ReactThreeFiber(R3F)で記述

ディレクトリ構成

─ public
    └ glsls
        ├ testfrag,glsl
        └ testvertex.glsl

- src
    ├ App.tsx
    └ components
        ├ TestShaders.tsx
        └ GLSLManager.ts

App.tsxの記述

import { Canvas } from "@react-three/fiber";
import { Environment } from "@react-three/drei";
import { TestShaderComponent } from "./components/TestShaders";

return (
        <div style={{ height: "90vh" }}>
            <Canvas shadows>
                <TestShaderComponent/>
                <Environment preset="dawn" background blur={0.7} />
                <directionalLight position={[100, 100, 100]} intensity={0.8} castShadow />
            </Canvas>
        </div>
    )

GLSLManager.tsの記述

import { createContext } from "react";

export class GLSLManager {
    Ready    : boolean = false;
    dirName  : string = "./glsls/";
    fileList : string[] = [];
    GLSLList : {[key: string]: string} = {};

    constructor(){}

    /**
     * GLSLファイルをロードする
     * @param fileList 
     * @returns 
     */
    async load(fileList: string[]){
        this.Ready = false;
        this.fileList = fileList;
        await (async () => {
            await Promise.all(fileList.map(async file => {
                await fetch(this.dirName+file)
                .then(response => response.text())
                .then(text => {
                    this.GLSLList[file] = text;
                });
            }))
        })()
        this.Ready = true;
    }

    /**
     * 特定のファイルのGLSLファイルの中身を取得
     * @param fileName 
     * @returns 
     */
    getGLSLText(fileName: string): string {
        return this.GLSLList[fileName]? this.GLSLList[fileName]: null;
    }

    /**
     * 新しいGLSLファイルをセットする
     */
    async setGLSLFile(fileName: string){
        fetch(this.dirName+fileName)
          .then(response => response.text())
          .then(text => {
            this.GLSLList[fileName] = text;
        });
    }

}

export const GLSLManagerContext = createContext<GLSLManager>(null);

TestShaders.tsxの記述

import { OrbitControls } from "@react-three/drei";
import { useEffect, useState, useContext } from "react";
import { GLSLManager, GLSLManagerContext } from "./GLSLManager";

const Cube = () => {
    const glsl = useContext(GLSLManagerContext);
    return (
      <mesh castShadow>
        <boxGeometry args={[1, 1, 1]} />
        <shaderMaterial
          fragmentShader={glsl.getGLSLText("testfrag.glsl")}
          vertexShader={glsl.getGLSLText("testvertex.glsl")}
        />
      </mesh>
    );
};

export const TestShaderComponent = () => {

    // 読み込むGLSLファイルリスト
    const fileList: string[] = [
      "testfrag.glsl",
      "testvertex.glsl"
    ];

    const [glsl, setGLSL] = useState<GLSLManager>(null);

    useEffect(() => {
        (async () => {
            const _glsl = new GLSLManager();
            await _glsl.load(fileList);
            setGLSL(_glsl);
        })()
    }, []);

    return (
        <>
            {glsl &&
              <GLSLManagerContext.Provider value={glsl}>
                <Cube/>
              </GLSLManagerContext.Provider>
            }
            <OrbitControls/>
        </>
    )
}

GLSLの中身

testvertex.glsl

void main() {
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  modelPosition.y += sin(modelPosition.x * 4.0) * 0.2;

  vec4 viewPosition = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPosition;

  gl_Position = projectedPosition;
}

testfrag.glsl

varying vec2 vUv;

vec3 colorA = vec3(0.912,0.191,0.652);
vec3 colorB = vec3(1.000,0.777,0.052);

void main() {
  vec2 normalizedPixel = gl_FragCoord.xy/600.0;
  vec3 color = mix(colorA, colorB, normalizedPixel.x);

  gl_FragColor = vec4(color,1.0);
}

実行結果

image.png

おわりに

webpackでGLSLLoaderを使う方法もあるようですが、
webpackを使うほどじゃないかなと感じたので、useContextで済ませました。

今後、Yuri Artiukhさんの動画をみつつ、
ごりごり勉強していきたいなと思います。

おわり。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?