FragmentShaderを表示するreactコンポーネントの作り方


概要

CRAでプロジェクトを作成、プロジェクトにPackageを入れて、↑を表示するシンプルなReactコンポーネントの作成を行います。

環境

  • Mac
  • yarn/npx (CRAのグローバルインストールは非推奨のため)
  • TypeScript 3.5+ (オプション)

CRAでベースアプリを作成

# npxの場合
$ npx create-react-app myshader

# yarnの場合
$ yarn create react-app myshader


# npx & typescriptを利用する場合
$ npx create-react-app myshader --template typescript

# yarn & typescriptを利用する場合
$ yarn create react-app myshader --template typescript

大抵の場合は必要ないですが、一応CRAがちゃんとコードを生成している事を確認しておきます。

$ cd myshader

# yarn
$ yarn start

# npm
$ npm start

[TypeScriptの場合]

react-vfxをインストール

react-vfxをインストールします。
併せて、styled-componentsも入れておきます。CSSやSCSSまたはSASSが好きな方はインストールしなくても全く問題ないです。
ちなみ、react-vfxはtsで書かれているので、@typesファイルのインストールは不要です。

# yarn
$ yarn add react-vfx styled-components
$ yarn add -D @types/styled-components <-- if you use typescript

# npm
$ npm install react-vfx styled-components
$ npm install -D @types/styled-components <-- if you use typescript

コンポーネントを作成

一番最初の画像を表示するためのシンプルなコンポーネントを作成していきます。
名前はなんでも良いのですが、今回は分かりやすくMyShader.tsxとしました。
本当はフォルダを作るべきなんですが、目的はreact-vfxを試す事なので、src直下に作りました。

import React from "react";
import * as VFX from "react-vfx";
import styled from "styled-components";

const Content = styled.div`
  width: 100vw;
  height: 100vh;
`;

const metal = `
uniform vec2 resolution;
uniform float time;
void main()
{
    vec2 coord = gl_FragCoord.xy / resolution.xy;
    vec2 st = coord;
    vec3 line = vec3(0.0);

    coord *= 4.;

    float len;

    for (int i = 0; i < 15; i++) {
        len = length(vec2(coord.x, coord.y));
        coord.x += cos(coord.y + sin(len)) + cos(time * .07) * 0.2;
        coord.y += sin(coord.x + cos(len)) + sin(time * 0.1);
    }

    len *= cos(len * 0.4);

    len -= 10.;

    for (float i = 0.0; i < 5.0; i++) {
        len += 0.11 / abs(mod(st.x, 1.09 * i) * 200.) * 1.;
    }

    vec3 color = vec3(cos(len + 0.2) * 1.15, cos(len + 0.1), cos(len - 0.05));

    gl_FragColor = vec4(color, 1.0);
}
`;

const MyShader: React.FC = () => {
  return (
    <>
      <VFX.VFXProvider>
        <VFX.VFXSpan shader={metal}>
          <Content></Content>
        </VFX.VFXSpan>
      </VFX.VFXProvider>
    </>
  );
}

export default MyShader;

コード自体は非常にシンプルで、react-vfxをインポート
import * as VFX from "react-vfx";
FragmentShaderをリテラル内に記述して、metalという変数を定義
インポートしたvfxのコンポーネントを使って、metal(FragmentShader)をロード

Contentは表示するためだけのものなので、普通にCSSを書いて、インポートする方がstyled-componentsを使うより簡単かつ楽だと思います。

あとはこれをApp.tsxもしくはApp.jsにインポートして、を記述したのちに、アプリを起動すれば、FragmentShaderで記述されたアニメーションが表示されます。

ちなみに、metalの部分にShaderToyのコードを移植して、変数名等を修正すれば、簡単に色々なモノを試せます。ただし、公開する場合はライセンスに気をつける必要があります。

react-vfxサンプルページ

react-vfx自体もエフェクトを持っているので、Shaderを書かなくても面白いエフェクトをページに簡単に加える事ができます。
https://amagi.dev/react-vfx/

[text]

repo:https://github.com/koji/typescript/tree/master/shadereact
レポジトリを試す場合は下記を実行すれば、動きます。

$ cd shaderreact
$ yarn
$ yarn start

Dev.toに投稿したポストを日本語にして投稿してみました。
元の記事
https://dev.to/kojikanao/use-fragmentshader-with-reactjs-easily-react-vfx-52lm