見出し画像

十四、サイズの異なる複数の点を描画する。(ストライド版)

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

■このプログラムの解説は、次の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="stride.js"></script>
  </body>
</html>


■JavaScriptのコードです。↓


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

// フラグメントシェーダのプログラム
var FSHADER_SOURCE =
  'void main() {\n' +
  '  gl_FragColor = vec4(0.957, 0.957, 0.973, 1.0);\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.02, 0.043, 0.173, 1);

  // Canvasをクリアする
  gl.clear(gl.COLOR_BUFFER_BIT);

  // 点を描画する
  gl.drawArrays(gl.POINTS, 0, n);
}

function initVertexBuffers(gl) {
  var verticesSizes = new Float32Array([
    // 点の座標,サイズ
-0.50, 0.00, 5.03,
-0.42, 0.17, 3.85,
-0.13, 0.13, 3.73,
-0.12, -0.06, 2.71,
0.08, -0.18, 5.11,
0.23, -0.28, 4.41,
0.50, -0.28, 4.84
  ]);
  var n = 7;





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

  // 頂点の座標とサイズをバッファオブジェクトに書き込む
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexSizeBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesSizes, gl.STATIC_DRAW);

  var FSIZE = verticesSizes.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 * 3, 0);
  gl.enableVertexAttribArray(a_Position);  // バッファオブジェクトの割り当ての有効化

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

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

  return n;
}

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

■以下は私が検索する時に使うヤツです。
星の色。

50行目あたり。自作関数。

夜空の色。

座標データの、0番目を最初として、n個のデータを参照して、点を描く。

Float32Array は”型付き配列”。 JavaScriptの配列は文字列が使えたりするが、明らかに実数型のデータしか使わない部分には、型を指定した配列を使った方が処理が速くなるらしい。
北斗七星の座標データと、サイズのデータ。

【重要】バッファオブジェクトは、WebGLにおいて、グラフィックスのデータ(例えば、頂点の座標や色、法線など)を一時的に保存するための領域です。

vertexBufferの中身が負 (使えるデータが無い) の場合、-1を返す。

vertexBufferという頂点バッファオブジェクトを現在のバッファとしてバインドすることを意味します。

gl.ARRAY_BUFFERに、verticesの情報を書き込む。

BYTES_PER_ELEMENT は別シート参照

a_Positionの中身が負 (使えるデータが無い) の場合、-1を返す。

”五”などに出て来た、gl.vertexAttrib [1234] f () では、1つ(または1組)のデータしか、attribute変数に代入できない。
今回はいっぱい点があるので、gl.vertexAttribPointer を使っている。
gl.vertexAttribPointer(attribute変数の格納場所, サイズ(1~4。3次元なら3など), データの形式, 正規化するかどうか(通常はしない), データの間隔, 何番目のデータから読み込むか(通常は0番目));
gl.vertexAttribPointerの第3引数は以下のとおり。
gl.UNSIGNED_BYTE → Unit8Array用 → 符号なしバイト
gl.SHORT → Int16Array用 → 符号付単精度整数
gl.UNSIGNED_SHORT → Uint16Array用 → 符号なし単精度整数
gl.INT → Int32Array用 → 符号付整数
gl.UNSIGNED_INI → Uint32Array → 符号なし整数 (インデックスなどに使われる)
gl.FLOAT → Float32Array用 → 浮動小数点数

これはやらなくてもいいらしい。
バッファをバインドしたままだとかわいそうなので、解放してあげる的な。

gl.vertexAttribPointer()は、attribute変数にバッファオブジェクトを割り当てますが、”有効”にしないと使えない。
なので、gl.enableVertexAttribArray()で”有効”にする。

ストライド stride
一般的には、「歩幅」

前回の場合。下記の様に、頂点の座標の配列と、サイズの配列を分けて書いていました。
var vertices = new Float32Array([
-0.50, 0.00, -0.42, 0.17, -0.13, 0.13, -0.12, -0.06, 0.08, -0.18, 0.23, -0.28, 0.50, -0.28
]);
var n = 7; // 頂点数

var sizes = new Float32Array([
5.03, 3.85, 3.73, 2.71, 5.11, 4.41, 4.84 // 点のサイズ
]);

また、頂点座標の為の gl.vertexAttribPointer と、サイズの為の gl.vertexAttribPointer を別々にコーティングしていました。
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, 0, 0);

今回は、下記の様に、頂点座標とサイズを一つの配列にまとめて入れています。
var verticesSizes = new Float32Array([
-0.50, 0.00, 5.03, -0.42, 0.17, 3.85, -0.13, 0.13, 3.73, -0.12, -0.06, 2.71, 0.08, -0.18, 5.11, 0.23, -0.28, 4.41, 0.50, -0.28, 4.84
]);
var n = 7;

図形が複雑になると、頂点やサイズや色などのデータを別々に準備するのは相当大変だそうで、上記の様に一つにまとめるのがよいそうです。

配列の中に入れるデータ数は、数百万個ほどでやめておくのがよいそうです。

次に、今回は、gl.vertexAttribPointerの引数を下記の様にしています。
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0);
第1引数:attribute変数の格納場所。
第2引数:成分の数。今回はXとYなので、2。三次元にした場合は、3になる。
第3引数:データ形式
第4引数:データが整数の場合、trueにする。
第5引数:何バイトおきにデータを参照するかをしていする。詳細は下図。
第6引数:何バイト目のデータから参照するかを指定する。

サイズの場合は、頂点の場合と比べると、下記の赤色文字部分が異なる。
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);
第6引数が0でなく、FSIZE * 2 になっている。→FSIZE * 2の分は飛ばして (オフセットして) データを呼んでね、ということ。

以上です。

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