###openframeworks+ofxshadertoyでボリューム(Texture 3D)マップが使えない。
openframeworksでshadertoyのコードでGLSLが表示できるofxshadertoyというアドオンがあるが、(関連記事 https://qiita.com/quittardis/items/8ba902ebceeef93d55c8)
以前の投稿で調べたキューブマップもそうだが、https://qiita.com/quittardis/items/9e1abab3babfeb3e1206
ボリューム(Texture 3D)マップもそのままではシェーダーをコンパイルすることができない。
(関連記事 https://www.shadertoy.com/view/MtjBWz
そこでキューブマップの時と同様に調べてみた。GLSLはボリューム(Texture 3D)データを扱うことができるので、工夫すれば、ofxshadertoyのシェーダーにもボリュームデータを渡すことができるはずである。
上は、openframeworksとofxshadertoyで、なんとかボリュームの3Dノイズをシェーダに渡して表示できた結果である。まだ、https://www.shadertoy.com/view/MtjBWz などはコンパイルできるが、うまく表示できない。
途中ではあるが、送るデータの塩梅の問題のようである。追って報告したい。
###ofxShadertoy.cppの変更箇所
ofxShadertoy.cppの42行目の
"uniform float xOutputAlpha;\n"
の下に、キューブマップ用に前回 (https://qiita.com/quittardis/items/9e1abab3babfeb3e1206)
"uniform samplerCube cubemap;\n"
を追加したが、その下に、
"uniform sampler3D tex3D;\n"
を追加する。tex3Dの変数名は任意である。これでボリュームデータをシェーダー内で扱える。
###openframeworks上での追加
openframeworks上のofApp.cppに以下を追加する。ボリュームマップの画像データを読み込むためのものである。
shadertoy.comで使われているボリュームマップデータはこちらからダウンロードできる。
https://shadertoyunofficial.wordpress.com/
これをopenframeworksのbin下のdata下にvolumesというというフォルダを作成してその中に置く。
int XDIM = 32, YDIM = 32, ZDIM = 32;
unsigned int ofApp::LoadVolumeFromFile(const char* fileName) {
FILE *pFile = fopen(fileName, "rb");
if (NULL == pFile) {
printf("error:%s\n",fileName);
return false;
}
const int size = XDIM * YDIM * ZDIM * 4;
GLubyte* pVolume = new GLubyte[size];
fread(pVolume, sizeof(GLubyte), size, pFile);
fclose(pFile);
//
//load data into a 3D texture
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_3D, textureID);
// set the texture parameters
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, XDIM, YDIM, ZDIM, 0, GL_RGB, GL_UNSIGNED_BYTE, pVolume);
delete[] pVolume;
return textureID;
}
ヘッダーに、
unsigned int LoadVolumeFromFile(const char* fileName);
もお忘れなく。
###shadertoyのコード
void mainImage( out vec4 O, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;
float ratio = 1.0;
vec4 col = (uv.x < ratio)?texture(tex3D, vec3(uv.x, uv.y, sin(iTime/20.0))):vec4(0.0,0.0,0.0,1.0);
O = col;
}
この中のtex3Dが、上で定義したボリュームデータを受け渡しする変数である。
###openframeworksからシェーダーへのボリュームデータの渡し方
unsigned int tid = LoadVolumeFromFile(("data/volumes/" + <ファイル名>).c_str());
shadertoy[shdrNum].setUniform1i("tex3D", tid);
これで、上の画像を表示するので、ボリュームデータはopenframeworks側からシェーダーに送れているようである。
要は、データサイズと
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, XDIM, YDIM, ZDIM, 0, GL_RGB, GL_UNSIGNED_BYTE, pVolume);
のパラメータを正しく設定すれば、うまくいきそうである。追って報告いたします。
###その後
その後、https://www.shadertoy.com/view/MlXcD4
のコードで、これと同じ表示になるか色々試した結果、
(これはShaderToyのグレーのボリュームのノイズテクスチャーを使用している)
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);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RED, XDIM, YDIM, ZDIM, 0, GL_RED, GL_UNSIGNED_BYTE, pVolume);
ttps://www.shadertoy.com/view/MlXcD4
のコードのWeb上の表示と同様の表示になりました。
上のパラメータの違いは、const int size = XDIM * YDIM * ZDIM * 4;の最後の*4を除き(グレーなので)、
さらに、 GL_CLAMP -> GL_CLAMP_TO_EDGE にしたこと、
GL_RGB -> GL_RED にしたところです。
###その後のその後
ShaderToyの本家のhttps://www.shadertoy.com/view/MtjBWzの投稿をベースに、以下のようにコードを変更
した3Dテクスチャーのテストコードに使用した。
// volumetric variant of https://shadertoy.com/view/XlBBRR
#define rndB(U) texture(tex3D, U) // trilinear interpolation
void mainImage( out vec4 O, vec2 U )
{
vec2 R = iResolution.xy;
U = ( U+U - R ) / R.y;
vec3 V = .2*vec3(U,.1*iTime);
O = rndB(V); // trilinear
O = sin(10.*O);
//O = sqrt(O); // gamma correction
}
また、OF側のボリュームデータの渡し方は、以下の通り
FILE *pFile = fopen(fileName, "rb");
if (NULL == pFile) {
return false;
}
const int size = XDIM * YDIM * ZDIM * 4; // 32x32x32 4 channels, unit8
GLubyte* pVolume = new GLubyte[size];
fread(pVolume, sizeof(GLubyte), size, pFile);
fclose(pFile);
//load data into a 3D texture
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_3D, textureID);
// set the texture parameters
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, XDIM, YDIM, ZDIM, 0, GL_RGB, GL_UNSIGNED_BYTE, pVolume);
これで、動画で、rgbボリュームマップ(textre3D)の様子をチェックできる。https://youtu.be/6DuhtofVn_Q
###その後のその後のその後
上記のパラメータでは、https://www.shadertoy.com/view/tsdcRj
は表示できなかった。
そこで、パラメータを以下のように変更した。
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, XDIM, YDIM, ZDIM, 0, GL_RGBA, GL_UNSIGNED_BYTE, pVolume);
動画のリンクはこちらhttps://youtu.be/OD01scbd-ys