[OpenGL] FrameBufferとRenderBufferについてメモ


FrameBufferやRenderBufferなど、バッファという言葉はOpenGLやWebGLを扱っていると頻繁に目にします。
ある程度理解していますが、時間を置くとどうしても理解が曖昧になるので、いったん自分の理解の範囲内で備忘録としてまとめました。

なので、間違いや勘違いがあるかもしれません。（もしなにか間違ってたら指摘してくれるとうれしいです）

## Framebuffer Object

まず、FrameBuffer ObjectとRenderBufferについての簡単な関係図を書きました↓


![buffers.png](https://qiita-image-store.s3.amazonaws.com/0/5616/af5f1047-3738-9083-0337-fe59b60f36cf.png "buffers.png")


見て分かるように、FrameBuffer Objectは「Color Buffer」「Depth Buffer」「Stencil Buffer」を内包しています。
つまり、FrameBuffer Objectはこれらいくつかのバッファを統合するオブジェクトです。
それ自身に値を保持しているわけではありません。（いわゆる関係の紐付けを管理するマネージャー？）

### Texture Buffer

そして、実際のデータ（ピクセルの値）を保持するのが「RenderBuffer」と「Texture Buffer」です。
Texture Bufferは複数パスで画像を処理する際などに、いったんテクスチャにレンダリングし、その後に各種ピクセルの値を別のパスで読み出したり、ということに使ったりします。

### RenderBuffer

レンダーバッファはその名の通り、レンダリングされた結果を保持するバッファです。
保持できる値は各ピクセルの特定の値です。それが「Color」「Depth」「Stencil」です。
つまり、レンダーバッファをカラーバッファに紐付ければ色の情報が保持され、レンダーバッファをデプスバッファに紐付ければ深度値が保持される、というわけですね。

### つまり

ひとまずまとめると、FrameBuffer Objectは最大で3種類のRenderBuffer（かTextureBuffer）をアタッチできる、というわけです。
細かいことを抜きにすれば登場人物は「FrameBuffer Object」「RenderBuffer」「TextureBuffer」の3つですね。こう考えるとだいぶすっきりします。

そしてRenderBufferは「Color Buffer」「Depth Buffer」「Stencil Buffer」のどれかの役を担う、と。
実際のコード例を書いておきます。


## レンダーバッファにレンダリングする

```renderbuffer.m
static GLuint color_render_buffer;
static GLuint depth_render_buffer;
static GLuint frame_buffer;

void InitRenderbuffer(void) {
	// カラーバッファ用レンダーバッファを生成（Generate）
	glGenRenderbuffers(1, &color_render_buffer);
	glBindRenderbuffer(GL_RENDERBUFFER, color_render_buffer);

	// デプスバッファ用レンダーバッファを生成
	glGenRenderbuffers(1, &depth_render_buffer);
	glBindRenderbuffer(GL_RENDERBUFFER, depth_render_buffer);
	
	// フレームバッファを生成
	glGenFramebuffers(1, &frame_buffer);
	glBindFramebuffer(GL_FRAMEBUFFER, fraume_buffer);

	// フレームバッファにレンダーバッファを、カラーバッファとしてアタッチ
	glFramebufferRenderbuffer(GL_FRAMBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_render_buffer);

	// フレームバッファにレンダーバッファを、デプスバッファとしてアタッチ
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_render_buffer);

	// ... 中略 ...

	GLsizei count = sizeof(indices) / sizeof(indices[0]);
	glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0);
}
```

上記は、レンダーバッファとフレームバッファの関連付けを示したコード断片です。
例では、レンダーバッファとしてカラーバッファとデプスバッファを関連付けています。
`glBindFramebuffer`でフレームバッファをバインドすると、以降はバインドされたフレームバッファにレンダリングされます。（つまりオフスクリーンレンダリング）



## テクスチャにオフスクリーンレンダリング

```texture-rendering.m
static GLuint texture;

void InitTexture() {
	// テクスチャを生成
	glGenTexture(GL_TEXTURE_2D, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_2D_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_2D_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_2D_MAG_FLITER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_2D_MIN_FLITER, GL_LINEAR);

	// フレームバッファのカラーバッファとしてテクスチャを設定
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

	// ... 中略 ...

	GLsizei count = sizeof(indices) / sizeof(indices[0]);
	glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0);
}
```

中略部分は、各種行列のセットアップやシェーダへのデータ送信などです。
要は、`glFramebufferTexture2D`関数を使ってフレームバッファにテクスチャをアタッチしたあと、通常の手順でレンダリングを行うとテクスチャに書き込まれる、というわけです。

テクスチャに書き込まれた内容は、次のパスで通常のテクスチャと同様にテクセルを取り出せるので、それと合成することで複数のパスを経由した様々なエフェクトが実現できるようになる、というわけです。