TouchDesignerとGLSLでキューブマッピング
こんにちは。
TD上で、GLSLだけでキューブマッピングをしてみました。
キューブマッピングとは
背景に箱型にした画像を置き、それが写り込んでいるように球などを描画すること・・・だと思っています。
今回はフラグメントシェーダーだけで、頂点シェーダーを使わないので、レイマーチングという方法を用いました。
画像の読み込み
まずは画像をキューブマッピングに使える形で読み込みます。
TDでは、360度カメラなどで撮影した画像を読み込んで projection TOP を用いるとキューブ型に変換してくれます。
そのまま projection TOP を GLSL TOP につなぎ、
#define cubeImage sTDCubeInputs[0]
で読み込みます。
レイマーチング
レイマーチングとは、カメラから光線が出ていると考え、その光線がぶつかった物体から描画していくという手法です。この方法を使うと、平面に描いているのに立体的に見えます。
具体的には、for文を書くことで少しづつ光線を伸ばしていくイメージです。そのため、for文を回す回数が少ないとギザギザします。
最後に、光線がぶつかった場所に球を描画(今回は反射ベクトルを計算)して完了です。
#define cubeImage sTDCubeInputs[0] uniform float iTime; vec2 fragCoord = vUV.xy * iResolution; float zoom = 2.0; const float sphereSize = 1.0; float sphereFunc(vec3 p){ return length(p) - sphereSize; } out vec4 fragColor; void main() { float time = iTime; vec2 p = (fragCoord.xy * 2.0 - iResolution) / min(iResolution.x, iResolution.y); // camera vec3 cameraPos = vec3(0.0, 0.0, zoom); vec3 cameraDir = vec3(0.0, 0.0, -1.0); vec3 cameraUp = vec3(0.0, 1.0, 0.0); vec3 cameraSide = cross(cameraDir, cameraUp); float depth = 1.0; // レイマーチング vec3 ray = normalize(cameraSide * p.x + cameraUp * p.y + cameraDir * depth); float distance = 0.0; float rayLength = 0.0; vec3 rayPos = cPos; for(int i = 0; i < 64; i++){ distance = sphereFunc(rayPos); rayLength += distance; rayPos = cameraPos + ray * rayLength; } vec3 ref; vec3 normal = normalize(rayPos); if(abs(distance) < 0.001){ vec3 spherePos = vec3(0.0); ref = reflect(spherePos - cameraPos, normal); }else{ ref = normal; } color = texture(cubeImage, ref).rgb; fragColor = TDOutputSwizzle(vec4(color, 1.)); }
カメラの回転
せっかくなので、カメラを回転させます。位置と向きに回転行列をかければ回転します。
//回転行列 mat3 rotate(float angle){ float c = cos(angle); float s = sin(angle); return mat3(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); }