7
4

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 で色々な表現に挑戦する - 単色の板ポリを配置する

Last updated at Posted at 2023-12-02

この記事の概要

前回の記事では、まずは canvas を用意して単色で塗りつぶしました。

このままでは div 要素を配置して background-color を指定するのとなんら変わりません。
今回は、canvas 内に単色の板ポリを配置することまでを実施します。

全体像

ステップバイステップだと、途中まではエラーしか出ません。
そのため、いきなり完成したコードの全体像を載せます。

// 前回のコード
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);

// 前回のコード
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// 新たに追加した描画のためのコード
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

見た目はこのようになっています。

シェーダープログラムの作成

シェーダーというものを使って、どんな頂点をもつ要素に、どんな色を塗るかを指定します。
今回は記事タイトルにもあるように板ポリ(厚みを持たないただの四角い板)を赤一色で塗ります。

シェーダーの定義

シェーダーには vertex shaderfragment shader の 2 種類があります。
vertex shader に頂点を、fragment shader に色を指定します。

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); // 赤色
}`;

vertexShaderSource には in vec2 positiongl_Position = vec4(position, 0.0, 1.0) という記述があります。
これは後で JavaScript から頂点データを受け取り、平面的にマッピングするためのコードです。

gl_Position の 3 つ目の引数の 0.0 は奥行き方向を表しています。
いくつかの座標の配列を XY 平面にプロットする、と捉えていてください。

fragmentShaderSourceoutColor には r, g, b, a を指定しています。
Web デザインではよく HEX 値を使いますが、ここではそれぞれの値を 0 から 1 にマッピングすることに注意してください。

gl_Position が組み込みの変数であることや、void main() という関数が必須なことなど、初見だと難しく感じることも多いかもしれません。
まずはとりあえず真似をして、書式そのものに慣れるのも重要に思います。

シェーダーオブジェクトの作成とコンパイル

vertex shaderfragment shader のそれぞれのコードは。WebGLのシェーダーオブジェクトに関連付けられコンパイルされます。

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);

シェーダープログラムの作成とリンク

最終的に、シェーダーは 1 つのシェーダープログラムに結合されます。

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

バッファの作成と設定

バッファとは、頂点の位置や色といったデータを格納するためのメモリ領域です。
後で頂点データをシェーダーに渡すために使用します。

バッファの作成

新しいバッファオブジェクトを作成します。

const positionBuffer = gl.createBuffer();

バッファのバインド

先ほど作成した positionBuffer をアクティブなバッファとしてバインドします。

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

gl.ARRAY_BUFFER は、バッファが頂点のデータを含むことを示しています。

頂点データの設定

四角形の頂点データを Float32Array の配列に格納します。
このデータを gl.bufferData を使って、バッファに設定します。

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);

画面の横方向が -1 から 1、縦方向も -1 から 1 にマッピングされているので、この指定だと画面の半分の位置

配列の順番が大切で、例えば以下のように指定してしまうと……。

const positions = new Float32Array([
  -0.5, -0.5,
   0.5, -0.5,
   0.5,  0.5, // 4 行目と入れ替えた
  -0.5,  0.5, // 3 行目と入れ替えた
]);

結果がこうなってしまいます。

厳密に言えばこの後記載する TRIANGLE_STRIP の設定とも組み合わさってこの見た目になっているのですが、とにかく「頂点の順番は適当ではいけない」と認識しておけば大丈夫だと思います。

属性ポインタの設定

vertex shader で定義した position 属性にデータを結びつけています。

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

描画の実行

前回の記事のコードに 1 行追加しています。

gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

「三角形を組み合わせて、4 つの頂点を使って、四角形を描画する」といった内容です。

最後に

随分と長いコードを書きましたが、まだ見た目はこれです(記事の前半に載せた画像の再掲)。

しかも手元で動かしてもらうと分かると思いますが、赤い四角形のフチがボケボケ(?)です。
次はそのあたりを修正していきます。

リッチな表現を実装するまで、まだ先は長い……。

次の記事はこちらです。

7
4
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
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?