A voxel world.

はじめに 編集

previous tutorialでは、ボクセルのチャンクをレンダリングする方法を見てきました。 ただし、たとえ見えなかったものがあっても、すべてのボクセルが描画されていました。 また、隣接するボクセルが同じ色やテクスチャを共有している場合、いくつかの三角形をマージすることが可能です。 このパートでは、見えないものを除去することによって必要な頂点の量を減らすことを試みます。

見えないボクセル面を除く 編集

一般に、三角形が不可視であるかをすべての可能なカメラ位置において判断することは非常に困難です。 しかし、私たちのボクセル世界では、その側のすぐ隣に別のボクセルがあれば、ボクセルの片側が見えないことが確認できます。 その場合、そのボクセルを構成する2つの三角形の描画を省略できます:

  for(int x = 0; x < CX; x++) {
    for(int y = 0; y < CY; y++) {
      for(int z = 0; z < CZ; z++) {
        // Empty block?
        if(!blk[x][y][z])
          continue;

        // View from negative x, only draw if there is no block in front of it
        if(x > 0 && !blk[x - 1][y][z]) {
          vertex[i++] = byte4(x,     y,     z,     blk[x][y][z]);        
          vertex[i++] = byte4(x,     y,     z + 1, blk[x][y][z]);        
          vertex[i++] = byte4(x,     y + 1, z,     blk[x][y][z]);        
          vertex[i++] = byte4(x,     y + 1, z,     blk[x][y][z]);        
          vertex[i++] = byte4(x,     y,     z + 1, blk[x][y][z]);        
          vertex[i++] = byte4(x,     y + 1, z + 1, blk[x][y][z]);        
        }

        ...

この実装では、1つのチャンク内の視認性しかチェックしないことに注意しましょう。 複数のチャンクがある場合で、 {{{1}}}の場合、隣接するチャンクに視界を遮るボクセルがあるかどうかチェックする必要があります。

エクササイズ:

  • 同様に他の5方向についても、このチェックを実装してみましょう。
  • 完全に満たされているチャンクを考えると (blk[x][y][z] すべてがゼロでない)、保存される頂点は見えないものを除くことでどれくらいの数になるでしょうか?
  • 生成されるチャンクの頂点が最大数になるのはボクセルの設定をどのようにしたときでしょうか?
  • GL_TRIANGLESの代わりにGL_LINESでチャンクをレンダリングしてみましょう。

隣接する面をマージする 編集

隣接する三角形をマージすることを考えた様々なアルゴリズムがありますが、簡単な方法は、目に見えるボクセルが同じタイプの行に2つあるかを確認することです。 それが真であれば、その後2つの新しい三角形を追加する代わりに、前の2つの三角形を変更して現在のボクセルを同様にカバーします。 それを行える方法がこれです:

  for(int x = 0; x < CX; x++) {
    for(int y = 0; y < CY; y++) {
      bool visible = false;

      for(int z = 0; z < CZ; z++) {
        // Empty block?
        if(!blk[x][y][z]) {
          visible = false;
          continue;
        }

        // Check if we are the same type as the previous block, if so merge the triangles.
        if(visible && blk[x][y][z] == blk[x - 1][y][z]) {
          vertex[i - 5] = byte4(x,     y,     z + 1, blk[x][y][z]);        
          vertex[i - 2] = byte4(x,     y,     z + 1, blk[x][y][z]);        
          vertex[i - 1] = byte4(x,     y + 1, z + 1, blk[x][y][z]);        
        }

        else

        // View from negative x, only draw if there is no block in front of it
        if(x > 0 && !blk[x - 1][y][z]) {
          vertex[i++] = byte4(x,     y,     z,     blk[x][y][z]);        
          vertex[i++] = byte4(x,     y,     z + 1, blk[x][y][z]);        
          vertex[i++] = byte4(x,     y + 1, z,     blk[x][y][z]);        
          vertex[i++] = byte4(x,     y + 1, z,     blk[x][y][z]);        
          vertex[i++] = byte4(x,     y,     z + 1, blk[x][y][z]);        
          vertex[i++] = byte4(x,     y + 1, z + 1, blk[x][y][z]);        
          visible = true;
        } else {
          visible = false;
        }

        ...

visibleというフラグは、以前のボクセルが表示されていたかどうかを記録します。 もしそうなら、頂点バッファ内の前の2つの三角形がそのボクセルに全く属さないことを確認できます。 さて、内側のループ変数はほとんどが zなので、z座標だけが異なる2つのボクセルを見ています。 なので、前のボクセルの3つの頂点を拡張して、その境界線を現在のボクセル上にし、現在のボクセルをカバーする必要があります。 3つはその中の"z + 1"を持ちます。

エクササイズ

  • 他の5方向についても同様に、このチェックを実装してみましょう。
  • この特定のアルゴリズムを使用して、潜在的にどのくらい多くの頂点を保存することができるでしょうか?
  • このアルゴリズムは、一方向に沿ってのみボクセルの面をマージします。 同様に他の方向にそれらをマージするための簡単​​な方法を考えることができるでしょうか?
  • 隣り合うチャンクから三角形をマージすることができるでしょうか?
  • GL_TRIANGLESの代わりにGL_LINESでチャンクをレンダリングして、面がマージされているかを確認してみましょう。