Googleさんの WebGL2 のラッパー「SwissGL」を p5.js と組み合わせて動かす

はてなブックマークで見かけた、Googleさんの WebGL2 のラッパー「SwissGL」が気になって、少し試してみた話です（※ 以下は、GitHub のリポジトリの URL・説明文）。

●google/swissgl: SwissGL is a minimalistic wrapper on top of WebGL2 JS API. It's designed to reduce the amount of boilerplate code required to manage GLSL shaders, textures and framebuffers when making procedural visualizations or simulations.
　https://github.com/google/swissgl

今回の内容は、公式リポジトリの「クイックスタート」を p5.js Web Editor上で動かしてみるというものです。公式リポジトリのクイックスタートの部分をキャプチャしたものを、以下に掲載します。

![クイックスタート](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/13c7255f-3620-97a2-c925-b7f8aff9d625.png)

最終的には、この処理結果を p5.js で用意したキャンバス上に描画します。

## p5.js とは完全独立で動かす
まずは、「SwissGL」の描画を p5.js とは完全に独立させた形で動かしてみます。プログラム自体は、p5.js Web Editor上に用意される p5.js用のものに書いていきます。

### 最初に試した内容
#### HTML の内容とライブラリのアップロード
以下は、最初のお試しで用いた HTMLファイルの内容です。

p5.js Web Editor でデフォルトで用意されるものに、少し手を加えています。具体的には、「p5.sound を読み込んでいた行を、SwissGL の読み込みに置きかえたこと」と、「canvas要素を追加したこと（※ `<canvas id="c" width="600" height="400"></canvas>` という部分の追加）」です。

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
    <script src="./swissgl.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <meta charset="utf-8" />
  </head>
  <body>
    <main>
      <canvas id="c" width="600" height="400"></canvas>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>
```

ここで読み込んでいる「swissgl.js」は、公式リポジトリからダウンロードしたファイルを、p5.js Web Editor上にアップロードしたものです（※ 以下の画像で示したもの）。
![swissgl.jsを含んだ状態](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d0a66e6e-0607-3b56-91a9-ee411999db91.png)

#### JavaScript の処理
次に、JavaScript の内容を掲載します。

```javascript
function setup() {
  createCanvas(400, 400);

  const canvas = document.getElementById("c");
  const glsl = SwissGL(canvas);

  function render(t) {
    t /= 1000; // ms to sec
    glsl({ t }, `UV,cos(t*TAU),1`);
    requestAnimationFrame(render);
  }
  render(0.0);
}

function draw() {
  background(220);
}
```

p5.js の処理の setup() に含めてはいるものの、p5.js の処理とは完全に独立した状態です。

とりあえずのお試しで動作させてみると、以下のとおり問題なく動きました。

https://twitter.com/youtoy/status/1630228632848461824

## p5.js と組み合わせる
### 最終的な内容
その後、p5.js と組み合わせて動かすようにしました。

主に対応した内容は、以下の通りです。

- SwissGL用の canvas要素の準備を、HTML上ではなく javascript で行う（そして、要素の作成のみ行う）
- requestAnimationFrame() で行っていた描画処理を、draw() で行う
- drawingContext.drawImage() を用いて、SwissGL　での描画結果を p5.js のキャンバス上に描画する

### HTML の内容
HTML の内容は以下の通りです。最初のお試しと異なる部分は、その時に行った 2つの対応のうち canvas要素の追加は行っていない点です。

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
    <script src="./swissgl.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <meta charset="utf-8" />
  </head>
  <body>
    <main></main>
    <script src="sketch.js"></script>
  </body>
</html>
```

### JavaScript の内容
次に、JavaScript の内容を掲載して、その後に補足を書きます。

```javascript
let myCanvas, glsl;

function setup() {
  createCanvas(600, 400);

  myCanvas = document.createElement("canvas");
  myCanvas.width = 600;
  myCanvas.height = 400;

  glsl = SwissGL(myCanvas);
}

function draw() {
  background(220);

  const t = millis()/1000
  glsl({ t }, `UV,cos(t*TAU),1`);
  drawingContext.drawImage(myCanvas, 0, 0);
}
```

setup() の中の以下の部分で、canvas要素を作成しています。

```javascript
  myCanvas = document.createElement("canvas");
  myCanvas.width = 600;
  myCanvas.height = 400;
```

また、requestAnimationFrame() で render()内の処理を繰り返していた描画部分は、draw() による繰り返しの中で行うように変更しました。また、時間に関する変数 t は、const で宣言した変数を使う形にして、その値は p5.js の経過時間を扱う関数である「[millis()](https://p5js.org/reference/#/p5/millis)」を用いて変更していくようにしました。

```javascript
  const t = millis()/1000
  glsl({ t }, `UV,cos(t*TAU),1`);
```

そして SwissGL　で描画した結果の出力先も変更しました。
最初のお試しでは、p5.js で用意したキャンバスとは別のキャンバスに描画していたのを、drawingContext.drawImage() を用いて p5.js のキャンバスに描画しています。

このような変更を加えた後のバージョンの動作も試してみて、以下のとおり最初のお試しと同様の結果を得られました（以下のツイートでは、静止画になっていて分からないですが、動作としてはアニメーションしています）。

https://twitter.com/youtoy/status/1630779941553070083

## まとめ
今回、SwissGL　のクイックスタートで提示されていた内容を、p5.js と組み合わせて使うことができました。

SwissGL の公式リポジトリには、別のサンプルも出ていたり、APIドキュメントが掲載されていたりするので、それらも今後試していければと思います。