WebGL で色々な表現に挑戦する - とにもかくにも canvas に描画する

## この記事の概要

HTML, CSS だけと比べて、WebGL を用いると表現力がアップします。
しかし WebGL や canvas に関連する記事が割と少なく、頑張って学んでいる最中です。
学んだ内容について、備忘録を兼ねて記事を書いていきます。

`canvas` 要素は、ただ HTML 上に配置したところでなんの効力も発揮しません。
描画領域を提供するだけで、その領域内に描画するための内容は含まれないからです。

実際の描画は、JavaScriptを使用して `canvas` 要素の描画コンテキストに命令を送ることで行われるのですが、一度にたくさんのことをやろうとすると複雑になります。
そのため、この記事では非常に簡単な内容だけを説明します。

## バンドラーの設定

バンドラーは無くても動かせますが、今後の色々を考えてセットアップしておきます。
自分はパッケージマネージャーとして Bun を使っていて、Vite で環境を構築しました。

```sh
bunx create-vite
```

フレームワークは無し（Vanilla）で、TavaScriptを選択しました。

## Canvas 以外の準備

Vite の雛形を使った都合で不要なファイルが存在しています。
ひとまずこれらを削除しました。

- `src/counter.ts`
- `src/typescript.svg`

次に `index.html` を変更します。

```diff_html
  <body>
-   <div id="app"></div>
+   <canvas id="webgl-canvas"></canvas>
    <script type="module" src="/src/main.ts"></script>
  </body>
```

`src/style.css` は、ファイル自体は残したままで、デフォルトのスタイルをリセットする程度の内容に変更します。

```css
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

#webgl-canvas {
  display: block;
  height: 100dvh;
  width: 100dvw;
}
```

また、`src/main.ts` の中身を一旦すべて消します。

```diff_typescript
- import './style.css'
- import typescriptLogo from './typescript.svg'
- import viteLogo from '/vite.svg'
- import { setupCounter } from './counter.ts'
- 
- document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
-   <div>
-     <a href="https://vitejs.dev" target="_blank">
-       <img src="${viteLogo}" class="logo" alt="Vite logo" />
-     </a>
-     <a href="https://www.typescriptlang.org/" target="_blank">
-       <img src="${typescriptLogo}" class="logo vanilla" alt="TypeScript logo" />
-     </a>
-     <h1>Vite + TypeScript</h1>
-     <div class="card">
-       <button id="counter" type="button"></button>
-     </div>
-     <p class="read-the-docs">
-       Click on the Vite and TypeScript logos to learn more
-     </p>
-   </div>
- `
- 
- setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)
```

## Canvas そのものの準備

先ほど中身をすべて削除した `src/main.ts` に手を加えます。

:::note warn
分かりやすさ重視で型アサーションを使っています。
:::

```diff_javascript
+ const canvas = document.getElementById("webgl-canvas") as HTMLCanvasElement;
+ const gl = canvas.getContext("webgl2") as WebGL2RenderingContext;
+ 
+ console.log(gl);
```

コンソールを見てみます（見やすいように整形してます）。

```sh
WebGL2RenderingContext {
  canvas: canvas#webgl-canvas,
  drawingBufferWidth: 300,
  drawingBufferHeight: 150,
  drawingBufferColorSpace: 'srgb',
  unpackColorSpace: 'srgb'
}
```

`webgl` のコンテキストが無事に取得できています（まだ画面は真っ白ですが、それであっています）。

確認できたので削除します。

```diff_typescript
  const canvas = document.getElementById("webgl-canvas") as HTMLCanvasElement;
  const gl = canvas.getContext("webgl2") as WebGL2RenderingContext;

- console.log(gl);
```

次に、シーン描画のための処理を書きます。

```diff_typescript
  const canvas = document.getElementById("webgl-canvas") as HTMLCanvasElement;
  const gl = canvas.getContext("webgl2") as WebGL2RenderingContext;

+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
```

これで、画面が真っ白から真っ黒へ変わったと思います。

`clearColor` で指定した色で塗りつぶされ、指定できるのは `r, g, b, a` です。
今は `0.0, 0.0, 0.0, 1.0` を指定しているため真っ黒ですが、仮に `1.0, 0.0, 0.0, 1.0` を指定した場合は真っ赤になります。

一度、色々な色を指定してみてください。

## 最後に

今回はここまでです。

正直この内容であれば `div` 要素を配置して `background-color` を指定した方が早いし分かりやすいです。

しかし、これから先の複雑な表現のための足がかりなので、一旦堪えてください。

次の記事はこちらです。

https://qiita.com/xrxoxcxox/items/e4ea15a00c48a0c05d89