WebGL - 翻訳:3D空間内でのオブジェクトの移動

こんにちは、WebGLの志願者さんたち!今日は、3Dグラフィックスの世界への興奮人心的な旅に出発します。WebGLにおける変換(translation)の概念を探求します。これは「物を動かす」ということの洒落た言い方です。このチュートリアルの終わりまでに、あなたはオブジェクトをデジタルバレエのように画面いっぱいに踊らせることができるようになります。それでは、始めましょう!

WebGL - Translation

WebGLにおける変換とは?

トライアングルをチェスの駒のように動かす前に、コンピュータグラフィックスの文脈における変換が実際に何を意味するかを理解しましょう。

変換の基本

変換は、2Dまたは3D空間内でオブジェクトを一つの位置から別の位置に移動するプロセスです。デスクからカップを拾い、棚に置くようなものです。カップ(私たちのオブジェクト)は元の位置から新しい位置に移動します。

WebGLでは、数学を使ってこの移動を達成します。数学が得意でない方も心配しないでください。WebGLが大部分の重い作業を引き受けてくれます!

変換がなぜ重要か?

キャラクターが動けないゲームや、オブジェクトが動かない3Dモデリングソフトウェアを想像してみてください。すごく退屈ですよね?変換は、ユーザーの入力に応じるか、予め決められたアニメーションに従う動的な、インタラクティブなグラフィックスを作成するために必要です。

トライアングルを変換する手順

変換が何かを理解したので、簡単なトライアングルをWebGLで動かすプロセスをステップバイステップで見ていきましょう。

ステップ1:トライアングルを定義する

まず、トライアングルを作成する必要があります。WebGLでは、頂点(角の点)を使って形状を定義します。以下のようにトライアングルを定義します:

const vertices = [
0.0, 0.5,   // 上の頂点
-0.5, -0.5,  // 左下の頂点
0.5, -0.5   // 右下の頂点
];

これにより、頂点が (0, 0.5) にあり、底辺の角が (-0.5, -0.5) と (0.5, -0.5) にあるトライアングルが作成されます。

ステップ2:変換行列を作成する

トライアングルを動かすために、変換行列を作成する必要があります。この行列は、オブジェクトを各軸(x、y、z)にどれだけ動かすかをWebGLに伝えます。以下のように変換行列を作成します:

const translationMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, 0, 1
];

ここで txty は、x軸とy軸に動かす量です。

ステップ3:頂点シェーダーで変換を適用する

ここからが面白い部分です!頂点シェーダーを修正して変換を適用します。以下は変換を含むシンプルな頂点シェーダーです:

attribute vec2 a_position;
uniform mat4 u_translation;

void main() {
gl_Position = u_translation * vec4(a_position, 0, 1);
}

このシェーダーは、各頂点の位置を取得し、変換行列と掛け算して新しい位置を計算します。

ステップ4:変換値を更新する

トライアングルを動かすために、変換値を時間とともに更新する必要があります。以下のようにJavaScriptコードで行います:

function updateAndDraw() {
tx += 0.01;  // 右に動かす
ty += 0.005; // 上に動かす

// 変換行列を更新
gl.uniformMatrix4fv(translationLocation, false, translationMatrix);

// トライアングルを描画
gl.drawArrays(gl.TRIANGLES, 0, 3);

requestAnimationFrame(updateAndDraw);
}

この関数は変換値を更新し、新しい行列をGPUに送信し、トライアングルを再描画します。requestAnimationFrameは、この処理をスムーズにフレームごとに行うために使用されます。

トライアングルを変換する例

それでは、すべてをまとめてトライアングルを動かす完全な例を見てみましょう。このコードは、画面対角線上に動く赤いトライアングルを作成します:

// 頂点シェーダー
const vertexShaderSource = `
attribute vec2 a_position;
uniform mat4 u_translation;

void main() {
gl_Position = u_translation * vec4(a_position, 0, 1);
}
`;

// フラグメントシェーダー
const fragmentShaderSource = `
precision mediump float;

void main() {
gl_FragColor = vec4(1, 0, 0, 1);  // 赤色
}
`;

// WebGLを初期化
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');

// シェーダーを作成しコンパイル
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);

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

// プログラムを作成
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);

// バッファを作成し頂点データを読み込む
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// 属性を設定
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// ユニフォームを設定
const translationLocation = gl.getUniformLocation(program, "u_translation");

// 変換変数
let tx = 0;
let ty = 0;

function updateAndDraw() {
// キャンバスを消去
gl.clear(gl.COLOR_BUFFER_BIT);

// 変換を更新
tx += 0.01;
ty += 0.005;

// 変換行列を作成
const translationMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, 0, 1
];

// 行列をシェーダーに送信
gl.uniformMatrix4fv(translationLocation, false, translationMatrix);

// 描画
gl.drawArrays(gl.TRIANGLES, 0, 3);

// 次のフレームをリクエスト
requestAnimationFrame(updateAndDraw);
}

// アニメーションを開始
updateAndDraw();

このコードは赤いトライアングルを画面対角線上に動かします。以下にコードの説明を示します:

  1. シェーダーを定義します。
  2. WebGLを初期化し、シェーダーを作成しコンパイルします。
  3. プログラムを作成し、シェーダーをアタッチしてリンクします。
  4. バッファを作成し、頂点データを読み込みます。
  5. 属性とユニフォームを設定します。
  6. updateAndDraw関数で、変換値を更新し、トライアングルを描画します。
  7. requestAnimationFrameを使用して、アニメーションをスムーズにします。

これで、あなたはトライアングルを動かすことができました。おめでとうございます!

終論

WebGLにおける変換は初めは複雑に見えるかもしれませんが、オブジェクトをスマートに動かすだけの話です。ここでは基本をカバーしましたが、変換を回転やスケーリングと組み合わせて、複雑なアニメーションやインタラクティブな3D環境を作成することもできます。

忘れないでください、すべての旅は一歩から始まります。この一歩を踏み出し、練習を続け、実験を続けると、あなたも驚くような3Dグラフィックスを作成できるようになるでしょう。

ハッピーコーディングを、そしてあなたのトライアングルがいつも家に帰れるよう祈っています!

メソッド 説明
gl.createShader() シェーダーオブジェクトを作成します
gl.shaderSource() シェーダーのソースコードを設定します
gl.compileShader() シェーダーをコンパイルします
gl.createProgram() プログラムオブジェクトを作成します
gl.attachShader() シェーダーをプログラムにアタッチします
gl.linkProgram() プログラムをリンクします
gl.useProgram() 指定されたプログラムを現在のレンダリングステートとして設定します
gl.createBuffer() バッファーオブジェクトを作成します
gl.bindBuffer() バッファーオブジェクトをターゲットにバインドします
gl.bufferData() バッファーオブジェクトのデータストアを初期化および作成します
gl.getAttribLocation() 属性変数の位置を取得します
gl.enableVertexAttribArray() ヴェクター属性配列を有効にします
gl.vertexAttribPointer() ヴェクター属性データのレイアウトを指定します
gl.getUniformLocation() ユニフォーム変数の位置を取得します
gl.clear() バッファーを预设の値にクリアします
gl.uniformMatrix4fv() ユニフォーム変数の値を設定します
gl.drawArrays() 配列データからプリミティブをレンダリングします

Credits: Image by storyset