--shader(GLSL)の小技 05-- 距離関数でバウンディングボックスを使う
raymarchigの進行を高速にする為のバウンディングボックスというtrick。raymarchigはモデルが増えれば増える程、GPUの負荷が増大する。そこで、モデルをグループにしてバウンディングボックスに納めるtrickを作ってみた。モデルが少ない時は邪魔ですが多い時は威力を発揮します。また、モデルを幾つかのグループにするのも有効です。
if (any(greaterThan(abs(p), bb))) return length(max(abs(p)-bb,0.0)) + eps;
この部分がそれにあたります。
greaterThan - OpenGL 4 Reference Pages
any - OpenGL 4 Reference Pages
使い方はこんな感じです。
float map(vec3 p) { // この定数は0.01くらいで良いのですが、trickの可視化の為に、この0.0005を用意しました。 float eps = float[](0.0005, 0.01)[int(step(2.0,mod(iTime,4.0)))]; // boundingBox // rayがbounding box内に進入してない時はboxの距離関数+epsを返す vec3 bb =vec3(3.5,3.5,1.5); if (any(greaterThan(abs(p), bb))) return length(max(abs(p)-bb,0.0)) + eps; // 進入したら細かいパーツ群の距離関数を返す。 // knotの軌道に30個の球を配置。 float de = 1.0; for (float i=0.; i < 30.; i++) { float a = i/30.0*radians(360.0); //radians(360.0)はPI*2の代わりに良く使ってます。 vec3 b =knot(a)-p; de = min(de, length(b)-0.5); } return de; }
今回のshader (shadertoy rule)
mat2 rotate(float a) { return mat2(cos(a),sin(a),-sin(a),cos(a)); } vec3 knot(float a) { return vec3( sin(a)+2.0*sin(2.0*a), cos(a)-2.0*cos(2.0*a), -sin(3.0*a) ); } float map(vec3 p) { // この定数は0.01くらいで良いのですが、trickの可視化の為に、この0.0005を用意しました。 float eps = float[](0.0005, 0.01)[int(step(2.0,mod(iTime,4.0)))]; // boundingBox // rayがbounding box内に進入してない時はboxの距離関数+epsを返す vec3 bb =vec3(3.5,3.5,1.5); if (any(greaterThan(abs(p), bb))) return length(max(abs(p)-bb,0.0)) + eps; // 進入したら細かいパーツ群の距離関数を返す。 // knotの軌道に30個の球を配置。 float de = 1.0; for (float i=0.; i < 30.; i++) { float a = i/30.0*radians(360.0); //radians(360.0)はPI*2の代わりに良く使ってます。 vec3 b =knot(a)-p; de = min(de, length(b)-0.5); } return de; } vec3 doColor(vec3 p) { return vec3(0.7,0.3,0.2); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv=(fragCoord*2.0-iResolution.xy)/iResolution.y; vec3 ro=vec3(0,3,-12); ro.zx*=rotate(iTime*0.5); vec3 rd=normalize(vec3(uv,2)); vec3 w=normalize(-ro); vec3 u=normalize(cross(w,vec3(0,1,0))); rd=mat3(u,cross(u,w),w)*rd; float d,i,t=0.0; vec3 p=ro; vec3 col=vec3(0); for(i=1.0;i>0.0;i-=1./50.0) { t+=d=map(p); if(d<0.001) { col=doColor(p); col*=i*i; break; } p+=rd*d; } fragColor=vec4(col,1.0); }