###openframeworks+ofxshadertoyでキューブマップが使えない。
openframeworksでshadertoyのコードでGLSLが表示できるofxshaderttoyというアドオンがあるが、
(関連記事 https://qiita.com/quittardis/items/8ba902ebceeef93d55c8
そのままでは、shadertoy.comにあるキューブマップ系のコードがシェーダーコンパイル時にエラーになる。
(関連リンク https://www.shadertoy.com/view/MdXfDS
そこで、いろいろやってみたところ、なんとか動いたので報告する。
(上は、shadertoy.comのhttps://www.shadertoy.com/view/tdjXDt をopenframeworksで動作させた画面)
(上は、shadertoy.comのhttps://www.shadertoy.com/view/4stXRN をopenframeworksで動作させた画面)
(上は、shadertoy.comのhttps://www.shadertoy.com/view/4sKGWV をopenframeworksで動作させた画面。マウスでぐるぐる回せます。)
###ofxShadertoy.cppの変更箇所
ofxShadertoy.cppの42行目の
"uniform float xOutputAlpha;\n"
の下に
"uniform samplerCube cubemap;\n"
を追加する。これは、シェーダー側で、キューブマップ用のマッピングデータを受け取る変数である。
###openframeworks上での追加
openframeworks上のofApp.cppに以下を追加する。これで、6枚のキューブマップの画像データを読み込む。
shadertoy.comで使われているキューブマップの画像データはこちらからダウンロードできる。
https://shadertoyunofficial.wordpress.com/
これをopenframeworksのbin下のdata下に置く。
unsigned int ofApp::loadCubemap(std::string fileName) {
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
for (unsigned int i = 0; i < 6; i++)
{
//get proper file name for each face from folder path -- assumes all files are named 'posX.jpg', etc
std::string fileNameTemp = fileName;
if (i == 0) { fileNameTemp.append("posX.png"); }
else if (i == 1) { fileNameTemp.append("negX.png"); }
else if (i == 2) { fileNameTemp.append("posY.png"); }
else if (i == 3) { fileNameTemp.append("negY.png"); }
else if (i == 4) { fileNameTemp.append("posZ.png"); }
else if (i == 5) { fileNameTemp.append("negZ.png"); }
ofImage image;
//image.load(fileNameTemp.c_str());
image.load(fileNameTemp);
int width = image.getWidth();
int height = image.getHeight();
//unsigned char *data = stbi_load(fileNameTemp.c_str(), &width, &height, &nrComponents, 0);
unsigned char *data = image.getPixels().getData();
if (data)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
//stbi_image_free(data);
}
else
{
//std::cout << "Cubemap texture failed to load at path: " << fileNameTemp[i] << std::endl;
std::cout << "Cubemap texture failed to load at path: " << fileNameTemp << std::endl;
//stbi_image_free(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;
}
ヘッダーには以下を加える。
'''
unsigned int loadCubemap(std::string fileName);
'''
###shadertoyのコードの変更箇所
たとえば、https://www.shadertoy.com/view/MdXfDS
のコードは、
vec3 getRayDirection(vec2 screenPosition, vec3 origin, vec3 lookingAt, vec3 up, float fov)
{
vec3 d = normalize(lookingAt - origin);
vec3 rayRight = normalize(cross(d, up));
return normalize(screenPosition.x * rayRight + screenPosition.y * up + 1.0 / tan(radians(fov / 2.0)) * d);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 position = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
vec3 cameraPosition = vec3(0.0, 0.0, 0.0);
vec3 cameraUp = vec3(0.0, 1.0, 0.0);
vec3 cameraLookingAt = vec3(cos(iTime), 0.0, sin(iTime));
vec3 rayDirection = getRayDirection(position, cameraPosition, cameraLookingAt, cameraUp, 90.0);
fragColor = vec4(texture(iChannel0, rayDirection).xyz, 1.0);
}
であるが、この17行目の fragColor = vec4(texture(iChannel0, rayDirection).xyz, 1.0);
でエラーになる。
このiChannel0をcubemapに変更し、
fragColor = vec4(texture(cubemap, rayDirection).xyz, 1.0);
とする。
###openframeworksからキューブマップデータをロードして、シェーダーに渡す。
openframeworksからキューブマップデータをロードして、シェーダーに渡す方法は
以下の通り
unsigned int cubemapTexture = loadCubemap("cubemaps/" + <キューブマップフォルダ名> + "/");
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
shadertoy[shdrNum].setUniform1i("cubemap", cubemapTexture);
つまり、cubeMaptextureはキューブマップ画像に紐づけられたunsigned intの値で、これを
setUniform1i でシェーダーの変数cubemapに渡している。