見出し画像

十七、四角形にテクスチャを貼る。

■サンプルページはコチラ↓

■このプログラムの解説は、次のExcelファイルをご覧下さい。

■htmlのコードです。↓

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>四角形にテクスチャを貼る</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">

    </canvas>

    <script src="webgl-utils.js"></script>
    <script src="webgl-debug.js"></script>
    <script src="sano-func.js"></script>

    <script src="QUAD-TEX.js"></script>
  </body>
</html>


■JavaScriptのコードです。↓


// 頂点シェーダのプログラム
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec2 a_TexCoord;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  v_TexCoord = a_TexCoord;\n' +
  '}\n';

// フラグメントシェーダのプログラム
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'uniform sampler2D u_Sampler;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
  '}\n';

function main() {
  // Canvas要素を取得する
  var canvas = document.getElementById('webgl');

  // WebGL描画用のコンテキストを取得する
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('WebGLコンテキストの取得に失敗');
    return;
  }

  // シェーダを初期化する
  if (!initShaders2(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('シェーダの初期化に失敗');
    return;
  }

  // 頂点情報を設定する
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('頂点情報の設定に失敗');
    return;
  }

  // Canvasをクリアする色を設定する
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // テクスチャを設定する
  if (!initTextures(gl, n)) {
    console.log('テクスチャの設定に失敗');
    return;
  }
}

function initVertexBuffers(gl) {
  var verticesTexCoords = new Float32Array([
    // 頂点座標、テクスチャ座標
    -0.5,  0.5,   0.0, 1.0,
    -0.5, -0.5,   0.0, 0.0,
     0.5,  0.5,   1.0, 1.0,
     0.5, -0.5,   1.0, 0.0,
  ]);
  var n = 4; // 頂点数

  // バッファオブジェクトを作成する
  var vertexTexCoordBuffer = gl.createBuffer();
  if (!vertexTexCoordBuffer) {
    console.log('バッファオブジェクトの作成に失敗');
    return -1;
  }

  // 頂点座標、テクスチャ座標をバッファオブジェクトに書きこむ
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

  var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
  // a_Positionの格納場所を取得し、バッファオブジェクトを割り当て、有効化する
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('a_Positionの格納場所の取得に失敗');
    return -1;
  }
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
  gl.enableVertexAttribArray(a_Position);  // バッファオブジェクトの割り当ての有効化

  // テクスチャ座標をa_TexCoordに割り当て、有効化する
  var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
  if (a_TexCoord < 0) {
    console.log('a_TexCoordの格納場所の取得に失敗');
    return -1;
  }
  gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
  gl.enableVertexAttribArray(a_TexCoord);  // バッファオブジェクトの割り当ての有効化

  // バッファオブジェクトのバインドを解除する
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  return n;
}

function initTextures(gl, n) {
  var texture = gl.createTexture();   // テクスチャオブジェクトを作成する
  if (!texture) {
    console.log('テクスチャオブジェクトの作成に失敗');
    return false;
  }

  // u_Samplerの格納場所を取得する
  var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
  if (!u_Sampler) {
    console.log('u_Samplerの格納場所の取得に失敗');
    return false;
  }

  var image = new Image();  // 画像オブジェクトを作成する
  if (!image) {
    console.log('画像オブジェクトの作成に失敗');
    return false;
  }
  // 画像の読み込み完了時のイベントハンドラを設定する
  image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };
  // 画像を読み込む(ブラウザにお任せ)
  image.src = 'kani.jpg';

  return true;
}

function loadTexture(gl, n, texture, u_Sampler, image) {
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);  // 画像のY軸を反転する
  // テクスチャユニット0を有効にする
  gl.activeTexture(gl.TEXTURE0);
  // テクスチャオブジェクトをターゲットにバインドする
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // テクスチャパラメータを設定する
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  // テクスチャ画像を設定する
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
  
  // サンプラにテクスチャユニット0を設定する
  gl.uniform1i(u_Sampler, 0);
  
  gl.clear(gl.COLOR_BUFFER_BIT);   // Canvasをクリアする

  gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // 四角形を描画する
}

■参考文献:WebGL+HTML5 3DCGプログラミング入門 (良い本ですが、絶版です。中古で購入される場合はCD-ROMがちゃんと付いているか確認して下さい)

■以下は私が検索する時に使うヤツです。(先程のExcelファイルに載っているので、読まなくて大丈夫です)

a_TexCoord は89行目に出てくる。

gl_FragColor は、フラグメントシェーダ内で定義される変数で、各ピクセルの色を決定します。
フラグメントシェーダの最後に、この変数に色を設定することによって、描画される最終的なピクセルの色を指定します。
texture2D は、GLSL ESの組み込み関数で vec4。テクスチャのサンプリング関数で、指定したテクスチャの特定の座標から色を取得します。
u_Sampler は、シェーダに送られるサンプラ変数で、使用するテクスチャを示します。
v_TexCoord は、テクスチャ座標を示す変数で、テクスチャ内のどの部分をサンプリングするかを指定します。

↑紫色の枠は、WebGLの座標。黒色の枠はテクスチャの画像。
①→②→③の三角形と、②→③→④の三角形で四角形を作っている。

vertexTexCoordBuffer の内容を、バッファオブジェクトに書きこむ

75行目で書き込んだ情報を読み取る方法を指定している。
ここで読み取っているのは、テクスチャ座標ではなく、頂点座標。

75行目で書き込んだ情報を読み取る方法を指定している。
ここではテクスチャ座標を読み取っている。
読み取った情報を a_TexCoord へ格納。
9行目の、 v_TexCoord = a_TexCoord; でシェーダに渡される。

gl.createTexture()は、WebGLのAPIの一部で、事前に定義されたメソッドです。

new Image()はJavaScriptの組み込み関数で、HTML DOM APIの一部です。
この関数は、HTMLドキュメント内で使用する画像オブジェクトを作成します。
Imageオブジェクトは、画像を読み込んだり、設定したりするための便利なオブジェクトです。

.onloadはImageオブジェクトのプロパティ。
画像が読み込まれた時に呼び出される関数を指定します。
loadTexture()の前に、無名関数function()を置くのは、loadTexture()を即時実行させたくないため。
無名関数function()を置くことで、画像を完全に読み込んでから処理が進むようにしている。

JPGなどの画像フォーマットの原点は、左上であることが多いです。
しかし、WebGLのテクスチャの原点は、左下です。
gl.pixelStorei の最後の i の文字はINTEGER の i 。
gl.pixelStorei の第一引数は他に3種類ほどあるが、説明省略。
gl.UNPACK_FLIP_Y_WEBGL は、Y軸を反転させるかどうか。 (1だと反転、0だとそのまま)

通常はTEXTURE7まであるらしい。画像を重ねる場合などに使う。8層あれば十分でしょう、ということらしい。
”テクスチャユニット”はステッカーを貼る枠のような物。
gl.TEXTURE0は初期状態で”有効”になっている為、gl.TEXTURE0のみの場合はこの文は無くてもよい。

第一引数は、gl.TEXTURE_2D か gl.TEXTURE_CUBE_MAP 。松田先生の書籍ではgl.TEXTURE_2D (=2次元のテクスチャ)しか使わない。
第二引数は、テクスチャオブジェクト。104行目で生成。

シェーダープログラムのサンプラーユニフォーム変数に、値を設定する。
サンプラー(sampler)は、シェーダープログラム内で、テクスチャデータにアクセスするための特殊なユニフォーム変数です。
今は u_Sampler がサンプラーユニフォーム変数。17行目と111行目で定義。
0はテクスチャユニットの番号。テクスチャユニットは133行目。
このコマンドの役割は、シェーダー内のu_Sampler変数をテクスチャユニット0にバインドすることです。
これにより、シェーダープログラムがu_Samplerを参照する際に、テクスチャユニット0にバインドされているテクスチャが使用されます。

gl.texParameteri
最後の”i”はINTEGERの”i”
gl.texParameterf もある。”f” はfloatの”f”
gl.texParameteri(target, pname, param);
・target: テクスチャ対象。gl.TEXTURE_2D か gl.TEXTURE_CUBE_MAP。
・pname: 設定するテクスチャパラメータ。下表参照。
・param: パラメータの値。下表参照。
テクスチャパラメータ
テクスチャパラメータ ←の説明 パラメータの値
TEXTURE_MAG_FILTER テクスチャ画像の拡大 gl.LINEAR デフォルト値。周囲のピクセルの色を線形補間。
”MAG”は”magnification”の略。線形補間しているので、NEARESTよりLINEARの方が見た目が良い。
gl.NEAREST テクスチャ座標が示す位置に物理的に最も近いテクセルの色を使用する。
TEXTURE_MIN_FILTER テクスチャ画像の縮小 gl.LINEAR 周囲のピクセルの色を線形補間。
gl.NEAREST テクスチャ座標が示す位置に物理的に最も近いテクセルの色を使用する。
gl.NEAREST_MIPMAP_NEAREST 最も近いミップマップレベルの中で最も近いピクセルを使用。
gl.NEAREST_MIPMAP_LINEAR デフォルト値。縮小に際し最適な2つのミットマップレベルが選択され、その2つのミットマップレベルの間で線形補間する。
TEXTURE_WRAP_S テクスチャがオブジェクトに対してs軸(水平)方向にどのように繰り返されるか、端の処理がどのように行われるかを制御する。 gl.REPEAT デフォルト値。テクスチャを繰り返し表示する。
gl.CLAMP_TO_EDGE テクスチャの端のピクセルを引き伸ばす。
gl.MIRRORED_REPEAT テクスチャを反転させて繰り返し表示。
TEXTURE_WRAP_T 上記をt軸(鉛直)で行う。 上に同じ 上に同じ。
ミップマップレベルについて。
元のテクスチャが10241024の場合、
ミットマップレベル0 → 元のサイズ = 10241024
ミットマップレベル1 → 元のサイズの半分 = 512512
ミットマップレベル2 → レベル1のサイズの半分 = 256256
ミットマップレベル3 → レベル2のサイズの半分 = 128128

ミットマップレベルX (最小) → 11まで。

gl.texImage2D
テクスチャオブジェクトに画像を設定する。
例 → gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
引数
target: gl.TEXTURE_2D か gl.TEXTURE_CUBE_MAP。
level: 使用するテクスチャがミップマップでない場合は、0。
使用するテクスチャがミップマップである場合は、そのミップマップのレベル。
internal format; テクスチャ画像内のフォーマット。内容は下表参照。
format: テクセルのフォーマット。internal format と同じにする。
type: テクセルのデータの型
image: Imageオブジェクト。
internal format と format で指定できる値
フォーマット
テクセルの構成
gl.RGB R、G、B。アルファ無し。JPGやBMP。
gl.RGBA R、G、B、A。PNGやGIF。
gl.ALPHA アルファのみ。アルファマスクとして使う。
gl.LUMINANCE 輝度。というかグレースケールにするらしい。
gl.LUMINANCE_ALPHA 輝度+アルファ。
typeで指定できる値
gl.UNSIGNED_BYTE デフォルト。各色成分が0から255の範囲で表現される8ビットの符号なし整数です。
gl.UNSIGNED_SHORT_5_6_5 RGBフォーマット用の16ビットデータ型で、5ビットの赤、6ビットの緑、5ビットの青の順。(ちなみに5ビットは0~31) ←画像を圧縮することになるが、通常は使用されない。
gl.UNSIGNED_SHORT_4_4_4_4 RGBAフォーマット用の16ビットデータ型で、各色成分が4ビットずつ格納されます。 ←画像を圧縮することになるが、通常は使用されない。
gl.UNSIGNED_SHORT_5_5_5_1 RGBAフォーマット用の16ビットデータ型で、5ビットの赤、5ビットの緑、5ビットの青、1ビットのアルファの順に格納されます。 ←画像を圧縮することになるが、通常は使用されない。
gl.FLOAT 32ビットの浮動小数点数で格納される型です(WebGL 1.0ではOES_texture_float拡張機能を使用する必要があります)。
gl.HALF_FLOAT_OES 16ビットの浮動小数点数で格納される型です(WebGL 1.0ではOES_texture_half_float拡張機能を使用する必要があります)。

フォーマット 戻り値
gl.RGB R、G、B、1.0
gl.RGBA R、G、B、A
gl.ALPHA 0.0、0.0、0.0、A
gl.LUMINANCE L、L、L、1.0
gl.LUMINANCE_ALPHA L、L、L、A

同期処理: 一つのタスクが完了するまで次のタスクを開始できません。タスクの順序が厳密に保たれますが、遅いタスクが全体を遅くすることがあります。
非同期処理: 複数のタスクを並行して進めることができ、特定のタスクが完了するのを待たずに他のタスクを進行させることができます。結果として効率が向上し、遅延を最小限に抑えられます。

以上です。

頂戴したサポートは、レンタルサーバーの費用に充てさせて頂きます🙇 心より感謝いたします❤️