WebGL - カラー:3Dグラフィックスに命を吹き込むための入門ガイド

こんにちは、WebGLの熱心な愛好家の皆さん!このカラフルな旅であなたのガイドを務めることができてとても嬉しいです。10年以上にわたってコンピュータグラフィックスを教えてきた者として、3Dシーンに色を加えることは、白黒写真に命を吹き込むようなものだと言えます。それは魔法のようで、今日はその魔法を一緒に解き明かしていきましょう!

WebGL - Colors

WebGLにおける色の理解

色を適用する具体的な方法に入る前に、まずWebGLの文脈で色とは何かを理解しましょう。このデジタルな領域では、色はRGB(赤、緑、青)色モデルで表現されます。各色はこれら3つの主要な色の組み合わせで、値は0.0から1.0の範囲です。

例えば:

  • 赤色: (1.0, 0.0, 0.0)
  • 緑色: (0.0, 1.0, 0.0)
  • 青色: (0.0, 0.0, 1.0)
  • 白色: (1.0, 1.0, 1.0)
  • 黒色: (0.0, 0.0, 0.0)

これを混色するのではなく、光で混色するようなものです。無限のパレットを持つデジタルアーティストのようなものです!

WebGLにおける色の適用

基本的なことを理解したところで、実際にWebGLで色を適用する方法を見ていきましょう。

色を適用する手順

  1. ヴェクターシェーダーで色の属性を定義する
  2. JavaScriptコードから色データをパスする
  3. フラグメントシェーダーで色を使用する

これらの手順をコード例とともに詳しく見ていきましょう。

ステップ1: ヴェクターシェーダーで色の属性を定義する

まず、ヴェクターシェーダーに色データを扱うことを伝えます。以下のようにします:

attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
v_Color = a_Color;
}

このコードでは、a_Colorという属性を定義し、v_Colorという変数を通じてフラグメントシェーダーに色データを渡します。

ステップ2: JavaScriptから色データをパスする

次に、JavaScriptコードからシェーダーに色データを送信します。以下はその例です:

// 頂点と色を定義
var vertices = new Float32Array([
0.0, 0.5, 1.0, 0.0, 0.0,  // 頂点1: x, y, r, g, b
-0.5, -0.5, 0.0, 1.0, 0.0,  // 頂点2: x, y, r, g, b
0.5, -0.5, 0.0, 0.0, 1.0   // 頂点3: x, y, r, g, b
]);

// バッファを作成し、データを送信
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// WebGLにバッファの読み方を伝える
var FSIZE = vertices.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);
gl.enableVertexAttribArray(a_Position);

var a_Color = gl.getAttribLocation(program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2);
gl.enableVertexAttribArray(a_Color);

このコードは少し複雑に見えるかもしれませんが、以下のように分解します:

  1. 頂点と色を単一の配列で定義します。各頂点には5つの値があります:x, y, r, g, b。
  2. バッファを作成し、データを送信します。
  3. バッファの読み方を指定します。位置(最初の2つの値)と色(最後の3つの値)。

これは、スーツケースに服と化粧品を詰め、友達にどのように開けるかを指示するようなものです!

ステップ3: フラグメントシェーダーで色を使用する

最後に、フラグメントシェーダーで色を使用します:

precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}

このシェーダーは、ヴェクターシェーダーから渡された色をフラグメント(ピクセル)に適用します。

色を適用する例

それでは、今までの内容をすべてまとめて、カラフルな三角形を作成する例を見てみましょう。

<!DOCTYPE html>
<html>
<head>
<title>カラフルなWebGL三角形</title>
</head>
<body>
<canvas id="glCanvas" width="640" height="480"></canvas>
<script>
// ヴェクターシェーダープログラム
const vsSource = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
v_Color = a_Color;
}
`;

// フラグメントシェーダープログラム
const fsSource = `
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;

function main() {
const canvas = document.querySelector("#glCanvas");
const gl = canvas.getContext("webgl");

if (!gl) {
alert("WebGLを初期化できません。あなたのブラウザや機器はサポートしていない可能性があります。");
return;
}

// シェーダープログラムを初期化
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

// 属性場所を取得
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, 'a_Position'),
vertexColor: gl.getAttribLocation(shaderProgram, 'a_Color'),
},
};

// バッファを作成
const buffers = initBuffers(gl);

// シーンを描画
drawScene(gl, programInfo, buffers);
}

function initBuffers(gl) {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

const positions = [
0.0,  0.5,  1.0, 0.0, 0.0,
-0.5, -0.5,  0.0, 1.0, 0.0,
0.5, -0.5,  0.0, 0.0, 1.0,
];

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

return {
position: positionBuffer,
};
}

function drawScene(gl, programInfo, buffers) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

gl.useProgram(programInfo.program);

{
const numComponents = 2;  // 1回のループで2つの値を取得
const type = gl.FLOAT;    // バッファ内のデータは32bitの浮動小数点数
const normalize = false;  // 正規化しない
const stride = 20;        // 次の値セットに進むために必要なバイト数
const offset = 0;         // バッファ内の開始位置
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexPosition);
}

{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 20;
const offset = 8;
gl.vertexAttribPointer(
programInfo.attribLocations.vertexColor,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexColor);
}

gl.drawArrays(gl.TRIANGLES, 0, 3);
}

function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

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

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert('シェーダープログラムを初期化できません:' + gl.getProgramInfoLog(shaderProgram));
return null;
}

return shaderProgram;
}

function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('シェーダーをコンパイルできません:' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}

return shader;
}

window.onload = main;
</script>
</body>
</html>

このコードを実行すると、赤、緑、青の頂点を持つ美しい三角形が表示されます。画面に虹が生き返るようなものです!

終章

そしてここまでが、WebGLのカラフルな世界を旅するガイドです。色の表現から3Dグラフィックスに色を適用する方法まで、基本的なことを学びました。これらの基本的な知識を持つことで、素晴らしい、鮮やかな3Dグラフィックスを作成するための道筋が開けました。

このまとめで思い出すのは、ある生徒が私に言った言葉です。「WebGLの色を学ぶことは、光で絵を描くようなものです。」彼女の言葉は正しいものでした。それでは、亲爱的な皆さん、デジタルのキャンバスに風の色で描きましょう!

快適なコーディングをし、WebGLの冒険が常にカラフルでありますように!

メソッド 説明
gl.clearColor(r, g, b, a) カラーバッファをクリアする色を設定
gl.clear(gl.COLOR_BUFFER_BIT) カラーバッファをクリア
gl.createBuffer() 新しいバッファオブジェクトを作成
gl.bindBuffer(gl.ARRAY_BUFFER, buffer) バッファオブジェクトをターゲットにバインド
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW) バッファオブジェクトのデータストアを作成および初期化
gl.getAttribLocation(program, name) 属性変数の位置を取得
gl.vertexAttribPointer(index, size, type, normalized, stride, offset) ヴェクター属性データのレイアウトを指定
gl.enableVertexAttribArray(index) ヴェクター属性配列を有効に
gl.useProgram(program) 指定されたプログラムを現在のレンダリングステートとして設定
gl.drawArrays(gl.TRIANGLES, 0, 3) 配列データからプリミティブをレンダリング

Credits: Image by storyset