thumb|A voxel world.
現代のCPUとGPUで利用可能な処理能力があれば、小さなキューブから構成される3Dの世界を完全にレンダリングすることができます。 
これを行うよく知られているゲームは Minecraft (次いでそれに触発されたInfiniminer) と [http://www.lexaloffle.com/voxatron.php Voxatron]です。
これらのゲームが備える独自の外観のほかにも、それ自体をグリッド上のキューブに制限することで、かなりの単純化を可能にしています。 
このチュートリアルシリーズでは、膨大な数のキューブを管理できる方法、それらを非常に効率的に描画できる方法について説明していきます。 
 
最初に行う重要なことは、レンダリングするボクセル世界を、管理しやすいチャンクに細分化することです。 
この方法なら、どのチャンクを画面上に表示するかを決定でき、そして見えないチャンクのレンダリングをスキップすることができます。 
また、メモリ·リソースをこの方法で管理することもできます。例えば、見えないチャンク頂点バッファオブジェクトをGPUメモリに保持する必要はありません。 
しかし、チャンクの追跡にほとんどの時間を費やすわけにはいかないので、十分な大きさでなければなりません。 
チャンクのサイズを変更可能にしていきますが、始めは比較的小さな16x16x16（4096）ブロックの塊にしましょう。 
ブロックの種類を格納するためにbyteを使用します。 
そのようなチャンクを表す構造体は次のようになります： 
struct chunk {
  uint8_t blk[CX][CY][CZ];
  GLuint vbo;
  int elements;
  bool changed;
  chunk() {
    memset(blk, 0, sizeof blk);
    vertices = 0;
    changed = true;
    glGenBuffers(1, &vbo);
  }
  ~chunk() {
    glDeleteBuffers(1, &vbo);
  }
  uint8_t get(int x, int y, int z) {
    return blk[x][y][z];
  }
  void set(int x, int y, int z, uint8_t type) {
    blk[x][y][z] = type;
    changed = true;
  }
  void update() {
    changed = false;
    // Fill in the VBO here
  }
  void render() {
    if(changed)
      update();
    // Render the VBO here
  }
};
blk配列は、ブロックタイプを保持します。 
get()とset()の関数は、どちらかといえば取るに足らないものですが、後で便利になるはずです。 
set()関数が呼び出されると、変更フラグがtrueに設定されます。 
チャンクがレンダリングされているとき内容が変更されると、これがupdate()関数の呼び出しをトリガーしてVBOを更新します。 
他のチュートリアルでは、GLfloat座標を頂点、テクスチャ、座標、色、などのためにも使用していました。 
OpenGLは、他のタイプも同様に使用することができます。 
私たちのボクセル世界では、すべてのキューブは同じ寸法を有しています。 
したがって、座標の表現に整数を使用することができます。 
世界は無限にできるにもかかわらず、1チャンク内であれば、可能性のあるすべての座標を表現するために、非常に小さな整数しか必要となりません。 
GLbytesを使用する場合、-128から+127までの範囲の座標を、持つことができ、16x16x16のチャンクには十分すぎます！ 
シェーダでは4つのコンポーネントまでのベクトルを使うことができ、X、Y、Zの座標のほかにも、 
情報の別のバイトを追加することができます。  私たちは、この "座標 "内にブロックの種類を格納します。 
後のチュートリアルでは、テクスチャ座標を導き出すためにこれを使用しますが、今のところは、それぞれのタイプに色を与えるために使用します。 
頂点ごとに4バイトの場面では、IBOを使用してもそれほど恩恵は多くありません。 
立方体の各面に異なる色や風合いを出すときや、IBOを使用するとき、実際にメモリ使用量が増加します。 
エクササイズ： 
私たちは、バイトの4次元ベクトルで作業していくことになります。 
残念なことに、それに利用するための事前に定義された型はありませんが、GLMは他のベクトル型と同様に、その役割を果たすものを手早く作成することができます： 
typedef glm::detail::tvec4 byte4;
さて、すべての頂点を保持するのに十分な大きさの配列を作成できているので、そこにつめ込んでいきましょう。 
キューブを作る方法はすでに知っているはずです。 
を使用することで、立方体の内部の面の描画を回避することができます。 
void update() {
  changed = false;
  byte4 vertex[CX * CY * CZ * 6 * 6];
  int i = 0;
  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
        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]);        
        // View from positive x
        vertex[i++] = byte4(x + 1, y,     z,     blk[x][y][z]);        
        vertex[i++] = byte4(x + 1, y + 1, z,     blk[x][y][z]);        
        vertex[i++] = byte4(x + 1, y,     z + 1, blk[x][y][z]);        
        vertex[i++] = byte4(x + 1, y + 1, z,     blk[x][y][z]);        
        vertex[i++] = byte4(x + 1, y + 1, z + 1, blk[x][y][z]);        
        vertex[i++] = byte4(x + 1, y    , z + 1, blk[x][y][z]);        
        // Repeat for y and z directions
        ...
      }
    }
  }
  elements = i;
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, elements * sizeof *vertex, vertex, GL_STATIC_DRAW);
}
エクササイズ： 
何かを描画する前には、ボクセルを描画するためのシェーダがここで必要になります。 
まず、頂点シェーダ： 
attribute vec4 coord;
uniform mat4 mvp;
varying vec4 texcoord;
void main(void) {
        texcoord = coord;
        gl_Position = mvp * vec4(coord.xyz, 1);
}
を通して頂点シェーダに入ります。 
として渡されます。 
を通じて、入力の座標を無変更でフラグメントシェーダに渡します。    
基本的なフラグメントシェーダは次のようになります： 
varying vec4 texcoord;
void main(void) {
  gl_FragColor = vec4(texcoord.w / 128.0, texcoord.w / 256.0, texcoord.w / 512.0, 1.0);
}
GL_BYTEsを使用していることに留意して、  "w" の座標は0 .. 255の範囲にしましょう。 
OpenGLは、これを魔法のように0 .. 1の範囲にマップすることはないので、フラグメントの色に使用可能な値を得るためにそれを割り算する必要があります。 
エクササイズ： 
頂点の場合だけでしょうか？ 
チャンク全体のレンダリングは、今は非常に簡単です。 
グラフィックスカードにVBOを指し示して、すべての三角形を描画してみましょう： 
void render() {
  if(changed)
    update();
  // If this chunk is empty, we don't need to draw anything.
  if(!elements)
    return;
  glEnable(GL_CULL_FACE);
  glEnable(GL_DEPTH_TEST);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glVertexAttribPointer(attribute_coord, 4, GL_BYTE, GL_FALSE, 0, 0);
  glDrawArrays(GL_TRIANGLES, 0, elements);
}
エクササイズ： 
し、カメラを配置し、それをレンダリングしましょう。 
さて、1チャンクを描画する方法がわかったので、いっぱいチャンクを描画してみたくなります。 
チャンクのコレクションを管理するには多くの方法があります。ただの三次元配列にしたり、または octree 構造にしたり、 
または任意のチャンクを保持するデータベースを持つこともできます。 
例えば、 Minecraftは、ハードディスク上にチャンクを格納し、チャンクの座標をファイル名にエンコードして、基本的にファイルシステムをデータベースとして使用しています。 
"superchunk"という、基本的には通常のチャンクへのポインタの3次元配列を作成してみましょう。 
非常に簡素にするなら、次のようになります： 
struct superchunk {
  chunk *c[SCX][SCY][SCZ];
  superchunk() {
    memset(c, 0, sizeof c);
  }
  ~superchunk() {
    for(int x = 0; x < SCX; x++)
      for(int y = 0; y < SCX; y++)
        for(int z = 0; z < SCX; z++)
          delete c[x][y][z];
  }
  uint8_t get(int x, int y, int z) {
    int cx = x / CX;
    int cy = y / CY;
    int cz = z / CZ;
    x %= CX;
    y %= CY;
    z %= CZ;
    if(!c[cx][cy][cz])
      return 0;
    else
      return c[cx][cy][cz]->get(x, y, z);
  }
  void set(int x, int y, int z, uint8_t type) {
    int cx = x / CX;
    int cy = y / CY;
    int cz = z / CZ;
    x %= CX;
    y %= CY;
    z %= CZ;
    if(!c[cx][cy][cz])
      c[cx][cy][cz] = new chunk();
    c[cx][cy][cz]->set(x, y, z, type);
  }
  void render() {
    for(int x = 0; x < SCX; x++)
      for(int y = 0; y < SCY; y++)
        for(int z = 0; z < SCZ; z++)
          if(c[x][y][z])
            c[x][y][z]->render();
  }
};
 関数を備え、そしてそれらの関数を適切なチャンク（複数可）に委譲します。 
関数は重要な機能を欠いています： 
各チャンクをレンダリングする前にモデル行列を変更する必要があり、そのようにして正しい位置に変換しないと、重なって描画されてしまいます： 
          if(c[x][y][z]) {
            glm::mat4 model = glm::translate(glm::mat4(1), glm::vec3(x * CX, y * CY, z * CZ));
            // calculate the full MVP matrix here and pass it to the vertex shader
            c[x][y][z]->render();
          }
エクササイズ： 
し、カメラを配置し、それをレンダリングしてみましょう。 
関数を使用して )。
OpenGL Programming/Glescraft 1