6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WebGL で色々な表現に挑戦する - canvas 自体のサイズや中身の描画を画面サイズにあわせる

Last updated at Posted at 2023-12-05

この記事の概要

前回の記事では 50 行くらいのコードを書いて、ようやく 1 枚の単色板ポリを生成しました。

しかし現状では描画がボケています。
具体的なグラフィックを作る前に、画面サイズにあわせた描画ができるようにします。

現状の確認

前回作った赤い四角形を拡大してみます。

ボケボケです。
今は単色なのでフチがボケているだけに見えますが、この状態でグラデーションやノイズを実装するとすべてがボケたグラフィックに仕上がります。

しかし、canvas 要素自体のサイズはちゃんと画面いっぱいに広がっています。
スクリーンショットの数値でいうと 1104 x 852 のサイズです。
最初の記事で、canvas に対して {height: 100dvh; width: 100dvw;} を指定していたので当然と言えば当然です。

ひとまず、canvas 自体のサイズには問題がないのに中身がボケている、ということをご認識ください。

解決策

  1. もともと // 描画の実行 とコメントをつけていた処理を関数にする → drawScene()
  2. 画面のサイズとcanvasのサイズ(描画バッファサイズ)を一致させる関数を作る → resizeCanvasToDisplaySize()
  3. resizeDOMContentLoaded にあわせて動かす
  // 描画の実行
- gl.clearColor(0.0, 0.0, 0.0, 1.0);
- gl.clear(gl.COLOR_BUFFER_BIT);
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+ function drawScene() {
+   gl.clearColor(0.0, 0.0, 0.0, 1.0);
+   gl.clear(gl.COLOR_BUFFER_BIT);
+   gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+ }
+ 
+ // ウィンドウサイズに合わせてcanvasのサイズを変更する
+ function resizeCanvasToDisplaySize() {
+   canvas.width = window.innerWidth;
+   canvas.height = window.innerHeight;
+   gl.viewport(0, 0, canvas.width, canvas.height);
+   drawScene();
+ }
+ 
+ // ウィンドウリサイズイベントに対するリスナーを設定
+ window.addEventListener("resize", resizeCanvasToDisplaySize);
+ 
+ // 初期表示時にもリサイズを実行
+ document.addEventListener("DOMContentLoaded", resizeCanvasToDisplaySize);

これにより、角がちゃんと綺麗になっています。

ポイントは resizeCanvasToDisplaySize() の部分です。
canvas 要素はサイズの概念に 表示サイズ描画バッファサイズ があります。

表示サイズ は通常 CSS で指定するようなサイズのことです。
先ほどのスクリーンショットで言えば 1104 x 852 と表示されていたサイズのことです。

描画バッファサイズcanvas 要素の中で実際に描画をするピクセル数です。
1 つ目の記事で WebGL コンテキストを取得した際、次のようになっていました。

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

drawingBufferWidth: 300, drawingBufferHeight: 150 とありました。
つまり 300 x 150 の要素を 1104 x 852 に引き延ばして表示している状態でした。

小さな画像を無理やり引き伸ばしてボケてしまった経験はあると思います。
それと同じことが起きていました。

1104 x 852 の要素を 1104 x 852 のサイズで表示するために、以下の処理を追加していた、というわけです。

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, canvas.width, canvas.height);

余談

ちなみに、常にウィンドウサイズと canvas のサイズが一緒になるように変えたため、CSS も少し簡略化できます。

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

この程度のコードならあってもなくてもそこまで変わりませんが、CSS と JavaScript の両方からサイズを指定する必要もないので消しました。

もっと言うなら getElementById するために指定した id をセレクターとしてスタイルを当てること自体イマイチではありますが、一旦このまま進めます。

最後に

ここまでのコードをすべて合わせると以下のようになります。

// canvas要素の取得とWebGL2コンテキストの取得
const canvas = document.getElementById("webgl-canvas") as HTMLCanvasElement;
const gl = canvas.getContext("webgl2") as WebGL2RenderingContext;

// シェーダープログラムの作成
const vertexShaderSource = `#version 300 es
in vec2 aPosition;
void main() {
  gl_Position = vec4(aPosition, 0.0, 1.0);
}`;

const fragmentShaderSource = `#version 300 es
precision mediump float;
out vec4 outColor;
void main() {
  outColor = vec4(1.0, 0.0, 0.0, 1.0); // 赤色
}`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER) as WebGLShader;
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) as WebGLShader;
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);

const shaderProgram = gl.createProgram() as WebGLProgram;
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);

// バッファの作成と設定
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

// canvasの幅と高さの半分の大きさの矩形の頂点データ
const positions = new Float32Array([
  -0.5, -0.5,
   0.5, -0.5,
  -0.5,  0.5,
   0.5,  0.5,
]);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

const positionAttributeLocation = gl.getAttribLocation(
  shaderProgram,
  "aPosition"
);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// 描画の実行
function drawScene() {
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

// ウィンドウサイズに合わせてcanvasのサイズを変更する
function resizeCanvasToDisplaySize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  gl.viewport(0, 0, canvas.width, canvas.height);
  drawScene();
}

// ウィンドウリサイズイベントに対するリスナーを設定
window.addEventListener("resize", resizeCanvasToDisplaySize);

// 初期表示時にもリサイズを実行
document.addEventListener("DOMContentLoaded", resizeCanvasToDisplaySize);

まだ、見た目は「真っ黒い背景に赤い板が浮かんでいるだけ」です。
あと 1, 2 記事したら見た目をいじるステップに進めるはず……。

次の記事はこちらです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?