ShaderToyのCubeMapAへの道、あるいは動的CubeMap入門
ShadertToyのスペシャルフューチャーとして、CubeMapAがある。
CubeMapAはhttps://shadertoyunofficial.wordpress.com/2016/07/20/special-shadertoy-features/
に解説があり、6枚のマッピング用のテクスチャーを用意しておく通常のCubeMapと違い、
シェーダーで動的にCubeMap用の画像を生成する機能である。以前のポストで、opennframeworksのアドオンであるofxshadertoyでCubeMapを実現する方法を実験した、
「ShaderToy+openframeworksでキューブマップ」
https://qiita.com/quittardis/items/9e1abab3babfeb3e1206
を参考にしていただきたい、
本稿では、まだ「ShaderToyのCubeMapAへの道」、つまり途上でなので最後までたどり着けるかどうかわからないが、CubeMapAを実現すべく試行錯誤を続けたい。
ofxshadertoyの開発者のtiagosor氏が、そうこうするうちにCubeMapAをインプリメントしてくれるかもしれないが、そうなっても、Shadertoyやopenframeworksを使わない一般的なOpenGLの動的キューブマップの勉強にもなればと考えている。
本稿は、上に述べた「ShaderToy+openframeworksでキューブマップ」をベースに進める。
OpenGLで動的マッピンテクスチャーマッピング
要するに、キューブマップ用の6枚テクスチャーをFramebufferに紐づけられたメモリに上書きできればいいわけで、このために、OpenGLのフレームバッファを準備し、キューブマップ用に用意されたテクスチャーとフレームバッファを紐づけて、テクスチャー直接描き変えるということが目標である。
Framebuffer
ヘッダーに、
GLuint FbCubeA;
GLuint TxCubeA;
とし、初期化するところに、
// CubeA用フレームバッファの準備
FbCubeA = 0;
glGenFramebuffers(1, &FbCubeA);
glBindFramebuffer(GL_FRAMEBUFFER, FbCubeA);
とする。
次以下のようににテクスチャーをアサインし、6枚のグレーのテクスチャーで
初期化する。
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
int width, height;
width = 512;
height = 512;
const int size = width * height * 3; // 4 channels, unit8
GLubyte* data = new GLubyte[size];
for (int i = 0; i < size; i+= 3) {
data[i] = 200;
data[i+1] = 200;
data[i+2] = 200;
}
キューブマップ用の6枚の画像の登録
次にこの6枚をキューブマップ用のテクスチャーとして登録する。
for (unsigned int i = 0; i < 6; i++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureID;
フレームバッファのカラーバッファとしてテクスチャを設定
このtextureIDをvoid ofApp::update()で以下のように利用する。
描画をスクリーンでなくテクスチャーの直接描くオフスクリーンレンダリングである。
xmove はintとしてglobalで定義され、描画色をフレームごとに変化させて、
TxCubeAは、グローバル変数で上で求めた、textureIDを代入する。
glBindFramebuffer(GL_FRAMEBUFFER, FbCubeA);
for (int face = 0; face < 6; face++)
{
// フレームバッファのカラーバッファとしてテクスチャを設定
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, TxCubeA, 0);
ofSetColor(255-(xmove%255), 0, 0);
xmove++;
ofDrawRectangle(10, 10, 512, 512);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
これで、キューブマップの画像を動的に変化させられることがわかった。
今回は描かれる画像が上下左右に切れ目なくつながった正式のキューブマップ用のテクスチャー
ではないので、これをシェーダーで描けるようになるのが次の目標である。
(Framebufferのほかにもデプスバッファを用意する場合もあるが、今回は3Dのオブジェクトを描画しないのでデプスバッファは不要)