ShaderToy+openframeworksでボリュームマップ

###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のシェーダーにもボリュームデータを渡すことができるはずである。

![out000016.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430586/e1cfda96-ef76-405d-fd2f-b8603d0c3046.jpeg)

上は、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);
```

というパラメータの組み合わせで、下図のように、![out000017.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430586/8375fa80-3841-8188-5dda-896f23e07e16.jpeg)

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の投稿をベースに、以下のようにコードを変更
した３Ｄテクスチャーのテストコードに使用した。

```
　// 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


![out000002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430586/f89f3fb3-8f63-b550-0985-788e166d32f6.jpeg)


###その後のその後のその後
上記のパラメータでは、https://www.shadertoy.com/view/tsdcRj
は表示できなかった。
そこで、パラメータを以下のように変更した。

```
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, XDIM, YDIM, ZDIM, 0, GL_RGBA, GL_UNSIGNED_BYTE, pVolume);
```

これによって下図のように表示できた。
![out000002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430586/4b0c6fd6-7800-f661-e171-29f850d7b624.jpeg)

動画のリンクはこちらhttps://youtu.be/OD01scbd-ys