WebGL - グラフィックスパイプライン

こんにちは、将来のプログラマーさんたち!今日は、WebGLグラフィックスパイプラインの興奮する旅に出発します。プログラミングが初めてであれば心配しないでください。私はあなたのフレンドリーなガイドとして、ステップバイステップで進めていきます。このチュートリアルの終わりには、WebGLがあなたのコードを画面上の素晴らしいビジュアルにどのように変換するかをしっかりと理解できるようになります。

WebGL - Graphics Pipeline

JavaScript:始点

まず、馴染みのあるJavaScriptから始めましょう。WebGLはJavaScriptを通じてアクセスされるため、私たちの冒険の完璧な出発点です。

最初のWebGLプログラム

簡単な例から始めましょう:

// canvas要素を取得
const canvas = document.getElementById('myCanvas');

// WebGLコンテキストを取得
const gl = canvas.getContext('webgl');

// クリアカラ(背景色)を設定
gl.clearColor(0.0, 0.0, 0.0, 1.0);

// キャンバスをクリア
gl.clear(gl.COLOR_BUFFER_BIT);

このコードスニペットでは、以下のことを行っています:

  1. HTMLキャンバス要素の参照を取得。
  2. WebGLレンダリングコンテキストを取得。
  3. クリアカラ(この場合は黒)を設定。
  4. 指定した色でキャンバスをクリア。

これは多くはないように見えるかもしれませんが、おめでとうございます!あなたは刚刚、最初のWebGLプログラムを作成しました。これはマスターピースのために準備された空白のキャンバスのようなものです。

ヴェルテックスシェーダ:世界を形作る

キャンバスが準備できたので、ヴェルテックスシェーダについて話しましょう。ヴェルテックスシェーダは、3D世界の彫刻師のようなものです。オブジェクトの生データ、すなわち頂点データを扱います。

簡単なヴェルテックスシェーダ

基本的なヴェルテックスシェーダの例を以下に示します:

attribute vec4 a_position;

void main() {
gl_Position = a_position;
}

このシェーダはシンプルですが非常に重要なことを行います。各頂点の位置を gl_Position に割り当てます。これは、3Dオブジェクトの各ポイントに「ここに来てください」と言うようなものです。

プリミティブアセンブリー:点を結ぶ

ヴェルテックスシェーダが仕事を終えた後、WebGLはプリミティブアセンブリーに進みます。このステージは「点を結ぶ」ようなもので、個々の頂点をどう結び合わせて形状を形成するかを判断します。

例えば、三角形を描く場合、プリミティブアセンブリーは3つの頂点を取り出し、それが単一の三角形を形成することを理解します。

ラスタライズ:いたるところにピクセル

次はラスタライズの魔法です。このステージは、3D形状を画面上の2Dピクセルに変換します。これは、3D彫刻を詳細な写真に撮るようなものです。

フラグメントシェーダ:世界を彩る

フラグメントシェーダは本当の芸術が行われる場所です。ヴェルテックスシェーダがオブジェクトの構造を扱っていたのに対し、フラグメントシェーダはそれを彩色します。

簡単なフラグメントシェーダ

すべてを赤く彩る基本的なフラグメントシェーダを以下に示します:

precision mediump float;

void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

このシェーダは gl_FragColor を赤色のベクトルに設定します(赤が最大、緑と青が0、不透明度が最大)。これは、3D世界全体を赤いペンキで塗るようなものです。

フラグメント操作:最後の仕上げ

フラグメントシェーダの後、WebGLはフラグメントに対して様々な操作を行います。これには深度テスト(どのオブジェクトが他のオブジェクトの前にあるかを判断する)、ブレンディング(透明なオブジェクトがどのように相互作用するか)などが含まれます。

フレームバッファ:大団円

最後に、フレームバッファに達します。これは、レンダリングされた画像が表示される前に保管される場所です。これは、カーテンが上がる前に最後の仕上げを施すバックステージのようなものです。

すべてを合わせる

各ステージを歩き抜けたので、それらがどのようにすべて一緒に働くかを完整的なWebGLプログラムで見てみましょう:

// ヴェルテックスシェーダのソースコード
const vsSource = `
attribute vec4 aVertexPosition;

void main() {
gl_Position = aVertexPosition;
}
`;

// フラグメントシェーダのソースコード
const fsSource = `
precision mediump float;

void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

// シェーダを初期化
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(fragmentShader);

// シェーダプログラムを作成
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

// プログラムを使用
gl.useProgram(shaderProgram);

// バッファを作成してデータを送信
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
1.0,  1.0,
-1.0,  1.0,
1.0, -1.0,
-1.0, -1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// WebGLが頂点データを頂点位置属性から引き出す方法を指定
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(
gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(gl.getAttribLocation(shaderProgram, 'aVertexPosition'));

// シーンを描画
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

このプログラムは、黒い背景に赤い四角形を描画します。以下に分解します:

  1. ヴェルテックスシェーダとフラグメントシェーダを定義。
  2. これらのシェーダをコンパイルし、リンクしてプログラムを作成。
  3. 頂点の位置を含むバッファを作成し、データを送信。
  4. バッファデータの解釈方法を指定。
  5. 最後に、画面をクリアし、四角形を描画。

各ステップは、私たちが話したグラフィックスパイプラインの各ステージに対応しています。これは、原材料(頂点データ)が最終製品(画面上のピクセル)に変換されるプロセスを見ているようなものです。

メソッドテーブル

以下は、私たちが使用した主要なWebGLメソッドの表です:

メソッド 説明
gl.createShader() シェーダオブジェクトを作成
gl.shaderSource() シェーダのソースコードを設定
gl.compileShader() シェーダをコンパイル
gl.createProgram() プログラムオブジェクトを作成
gl.attachShader() シェーダをプログラムにアタッチ
gl.linkProgram() プログラムをリンク
gl.useProgram() 指定されたプログラムを現在のレンダリング状態的一部分に設定
gl.createBuffer() バッファオブジェクトを作成
gl.bindBuffer() バッファオブジェクトをターゲットにバインド
gl.bufferData() バッファオブジェクトのデータストアを作成および初期化
gl.vertexAttribPointer() ヴェルテックスデータのレイアウトを指定
gl.enableVertexAttribArray() ヴェルテックス属性配列を有効に
gl.clearColor() カラーバッファをクリアする際に使用する色を設定
gl.clear() バッファを预设の値にクリア
gl.drawArrays() 配列データからプリミティブをレンダリング

そして、ここまでがWebGLグラフィックスパイプラインの旅です。JavaScriptから最終フレームバッファまで、各ステップを理解しました。WebGLをマスターするには練習が必要ですが、書いたコードの每一行で、ブラウザ内で素晴らしい3Dグラフィックスを作成する一歩近づくことができます。実験を続け、学び続け、そして何より楽しみましょう!

Credits: Image by storyset