p5.js の createFilterShader() を使ったフィルター用シェーダーのお試し

## はじめに
今回の内容は、p5.js の createFilterShader() のお試しです。

### createFilterShader() とは？
createFilterShader() がどういうものかという話は、以下の公式リファレンスで説明されています。

●reference | createFilterShader()
　https://p5js.org/reference/#/p5/createFilterShader

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/52b080bb-19e7-39e8-b0d1-57258dba993a.png)

冒頭の説明を見てみると、「using only a fragment shader, as a convenience method for creating image effects.」と書いてあります。
その説明内容は、「フラグメントシェーダーを利用するだけで、シェーダーの処理が行えて、画像エフェクトを使うのに便利」というものです。

#### p5.js でのシェーダーを使う場合の準備
「フラグメントシェーダーを利用するだけで...」という部分は、少し補足すると「バーテックスシェーダーの準備が不要」ということになります。
p5.js でシェーダーを利用する場合、createShader() を使う方法があり、そしてこの createShader() を使う場合は、バーテックスシェーダーとフラグメントシェーダーを準備する必要がありました。

●reference | createShader()
　https://p5js.org/reference/#/p5/createShader

createFilterShader() を使う場合は、バーテックスシェーダーが準備済みの状態となるようで、そのため自前で用意するのはフラグメントシェーダーだけとなるようです。

## 試してみた内容
### createFilterShader() を使った描画
ここで createFilterShader() を使った描画を先に掲載してみます。

以下の動画では、4つの図形が動いており、その中の図形 2つの色が途中で変化しています。
これは、画面の中のピクセルに対する処理をシェーダーを用いて行っているためです。

処理内容は、画面内のピクセルで RGB の G（緑）の値が一定以上の大きさの場合、色の変更を行っています。この変更処理の ON/OFF は、ユーザーの操作で実行するようにしています。そして、以下の動画では、その ON/OFF の切り替えを何度か行っています。

<iframe width="560" height="315" src="https://www.youtube.com/embed/YQUBPXHtAww?si=852XoxSvqcYxUpuc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### 実装内容
実際に実装した内容は以下のとおりです。

```javascript
const colorList = ["#3f616c", "#6baec5", "#9dcebc", "#e0e8a4"];

let isF = true;

function setup() {
  createCanvas(500, 400);
  noStroke();

  // ------------------------
  let fragSrc = `precision highp float;

  varying vec2 vTexCoord;
  uniform sampler2D tex0;

  void main() {
    vec4 color = texture2D(tex0, vTexCoord);
	if (color.g > 0.8) {
	  gl_FragColor = vec4(color.r = 0.85, color.g= 0.9, color.b = 0.95, 1.0);
	} else {
      gl_FragColor = vec4(color);
	}
  }`;
  s = createFilterShader(fragSrc);
  // ------------------------
}

function draw() {
  background(0);

  // 円1つ目
  const x = width * 0.5 + width * 0.22 * cos(frameCount / 25),
    y = height * 0.5 + height * 0.25 * sin(frameCount / 36);
  fill(colorList[0]);
  circle(x, y, height * 0.22);

  // 矩形
  const x2 = width * 0.6,
    y2 = height * 0.3 + height * 0.2 * sin(frameCount / 27);
  fill(colorList[1]);
  rect(x2, y2, height * 0.22);

  // 円2つ目（大きい方）
  push();
  translate(width / 2, height / 2);
  rotate(frameCount / 30);
  fill(colorList[2]);
  circle(50, 50, height * 0.33);
  pop();

  // 三角形
  push();
  translate(width / 2, height / 2);
  rotate(frameCount / 40);
  translate(width * 0.1, 0);
  fill(colorList[3]);

  triangle(0, 0, height * 0.35, 0, 0, height * 0.35);
  pop();

  if (isF) filter(s);
}

function mouseClicked() {
  isF = !isF;
}
```

上記の中で、createFilterShader() に関わる部分は、主に `  // ------------------------` で囲まれたところです。

その中でも、処理のメインの内容は以下です。
この処理では、キャンバス上の色を、デフォルトで準備された「vTexCoord と tex0」を用いて取得しています。

その取得した色を元に、 `if (color.g > 0.8)` の条件にマッチしたピクセルの色を `gl_FragColor = vec4(color.r = 0.85, color.g= 0.9, color.b = 0.95, 1.0);` という特定の色にしています。

```javascript
  void main() {
    vec4 color = texture2D(tex0, vTexCoord);
	if (color.g > 0.8) {
	  gl_FragColor = vec4(color.r = 0.85, color.g= 0.9, color.b = 0.95, 1.0);
	} else {
      gl_FragColor = vec4(color);
	}
  }`;
```

なお、シェーダーを適用する部分は、 draw() の中の `filter(s)` の部分です。

このようにして、createFilterShader() を使ったシェーダーの適用を行いました。

## 補足
### デフォルトで用意されるバーテックスシェーダーについて
バーテックスシェーダーはデフォルトで用意されると書きました。
それに関連して利用できる変数は、公式リファレンスに書かれた以下となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f6af1dc2-ec66-d6ea-f88c-56d3eb4c6d50.png)