Creating 3D objects using WebGL

by 3 contributors:

正方形に 5 つの面を追加して立体化し、キューブを作成しましょう。これを効率的に行うために、gl.drawArray() メソッドを呼び出して頂点情報を直接使用する描画方法から、頂点の配列をテーブルとして扱い、gl.drawElements() を呼び出して、そのテーブルの各頂点情報を参照して各面の頂点を定義する方法に切り替えます。

留意点: それぞれの面について 4 個の頂点を定義する必要がありますが、各頂点は 3 つの面で共有されます。24 個の頂点すべてのリストを構築することで、やりとりするデータを少なくすることができます。そして座標を指示するには、座標の完全なセットを渡すのではなく、リストのインデックスを用いて座標を指し示します。

キューブの頂点の位置の定義

始めに initBuffers() のコードを更新して、キューブの頂点の位置のバッファを作成します。この方法は正方形を作る場合と同じですが、24 個の座標 (1 面につき 4 個) がありますのでコードはとても長くなります:

  var vertices = [
    // Front face
    -1.0, -1.0,  1.0,
     1.0, -1.0,  1.0,
     1.0,  1.0,  1.0,
    -1.0,  1.0,  1.0,
    
    // Back face
    -1.0, -1.0, -1.0,
    -1.0,  1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0, -1.0, -1.0,
    
    // Top face
    -1.0,  1.0, -1.0,
    -1.0,  1.0,  1.0,
     1.0,  1.0,  1.0,
     1.0,  1.0, -1.0,
    
    // Bottom face
    -1.0, -1.0, -1.0,
     1.0, -1.0, -1.0,
     1.0, -1.0,  1.0,
    -1.0, -1.0,  1.0,
    
    // Right face
     1.0, -1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0,  1.0,  1.0,
     1.0, -1.0,  1.0,
    
    // Left face
    -1.0, -1.0, -1.0,
    -1.0, -1.0,  1.0,
    -1.0,  1.0,  1.0,
    -1.0,  1.0, -1.0
  ];

頂点の色の定義

24 個の座標それぞれについて、色の配列を作成する必要があります。このコードでは始めに各面の色を定義します。次にループを用いてこれらの配列を各頂点の色情報に変換しています。

  var colors = [
    [1.0,  1.0,  1.0,  1.0],    // Front face: white
    [1.0,  0.0,  0.0,  1.0],    // Back face: red
    [0.0,  1.0,  0.0,  1.0],    // Top face: green
    [0.0,  0.0,  1.0,  1.0],    // Bottom face: blue
    [1.0,  1.0,  0.0,  1.0],    // Right face: yellow
    [1.0,  0.0,  1.0,  1.0]     // Left face: purple
  ];
  
  var generatedColors = [];
  
  for (j=0; j<6; j++) {
    var c = colors[j];
    
    for (var i=0; i<4; i++) {
      generatedColors = generatedColors.concat(c);
    }
  }
  
  cubeVerticesColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);

エレメント配列の定義

頂点の配列を生成したら、エレメントの配列を作成する必要があります。

  cubeVerticesIndexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
  
  // This array defines each face as two triangles, using the
  // indices into the vertex array to specify each triangle's
  // position.
  
  var cubeVertexIndices = [
    0,  1,  2,      0,  2,  3,    // front
    4,  5,  6,      4,  6,  7,    // back
    8,  9,  10,     8,  10, 11,   // top
    12, 13, 14,     12, 14, 15,   // bottom
    16, 17, 18,     16, 18, 19,   // right
    20, 21, 22,     20, 22, 23    // left
  ]
  
  // Now send the element array to GL
  
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
      new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);

cubeVertexIndices 配列はそれぞれの面を 2 つの三角形として定義し、それら三角形の頂点はキューブの頂点の配列に対するインデックスで指定しています。よって、キューブは 12 個の三角形の集合体として表されます。

キューブの描画

次に drawScene() 関数にキューブのインデックスバッファを用いて描画するためのコードを追加する必要がありますので、新たな bindBuffer() および drawElements() の呼び出しを追加します:

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
  setMatrixUniforms();
  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);

キューブの各面は 2 個の三角形で構成されますので、1 面あたり 6 個・キューブ全体では 36個の頂点が存在することになります。ただし、それらの多くは重複しています。しかし、インデックスの配列は単なる数値のみで構成されていますので、アニメーションのフレームごとに渡すデータの量が過度に多くなることはありません。

ここで、6 つの面が鮮やかな色で塗られたキューブが回転しながら跳ね回るアニメーションが完成しました。WebGL 対応のブラウザを実行している場合は、こちらをクリックして デモを実行してみてください。

ドキュメントのタグと貢献者

タグ: 
Contributors to this page: fscholz, ethertank, yyss
最終更新者: fscholz,