--shader(GLSL)の小技 16-- 湾曲と補正
距離関数に入れる前の座標を湾曲させます。単純にすると歪がでるので、それを補正します。
ここにも載っていますが、違う方法でやってみます。
まず、プロットに使うような関数を用意します。
float func(float x) { float t = iTime*3.0; return 0.2*sin(x*3.0+t)+0.2*sin(x*2.0+t); }
これで空間を歪めます。
p.y -= func(p.x);
こうです。次に、この関数を微分します。微分は極小の勾配なので、
float e = 0.01; float g = (func(p.x+e)-func(p.x-e))/(2.0*e);
こうなると思っていたら、添削を受けました。
GLSL Sandbox をsandboxにupしたらforkされて
GLSL Sandbox こうなりました。
float g = (func(p.x+e)-func(p.x-e))/(PI*e);
この方が良さそうです。まあ、見た目の話ですので、どちらでも良いとは思いますが、お好みでどうぞ。
このg
を使って補正をかけます。
p.y *= cos(atan(g));
これについては、やはり適当にやって落ち着いた式です。数学的根拠はありません。良さげなので使ってます。 以前
Shader - Shadertoy BETA
ここで、ツッコまれました。
これで、空間の湾曲と補正が終わりです。あとは普通に距離関数を使うだけです。
この応用で
vec2 bend(vec2 p, float d) { p.y -= d*p.x*p.x; p.y *= cos(atan(2.0*d*p.x)); return p; }
こんなモノも作りました。ベジェ曲線はコストが高いので、2次関数で湾曲させます。ただし、両端は歪が取れないので誤魔化す様に使ってます。
また、2次元の湾曲もできます。
p.zy -= func(p.x); vec2 g = (func(p.x+e)-func(p.x-e))/(PI*e); p.zy *= cos(atan(g));
こんな感じで処理すれば、良いと思います。
このやり方でアーティファクトが出るので距離関数の出力に補正(*0.8)をかけてます。
return deSeg(p,2.0,0.2) *0.8;
アーティファクトの対処法として、1以下の数字を乗算する方法と、もう一つ
return min(0.4, deSeg(p,2.0,0.2));
なんてのもアリです。
今回のshader (shadertoy rule)
mat2 rotate(float a) { return mat2(cos(a),sin(a),-sin(a),cos(a)); } float func(float x) { float t = iTime*3.0; return 0.2*sin(x*3.0+t)+0.2*sin(x*2.0+t); } float deSeg(vec3 p, float h, float r) { p.x -= clamp( p.x, -h, h ); return length( p ) - r; } #define PI radians(360.0) float map(vec3 p) { float e = 0.01; p.y -= func(p.x); // 微分する。 float g = (func(p.x+e)-func(p.x-e))/(PI*e); // 補正 p.y *= cos(atan(g)); return deSeg(p,2.0,0.2) *0.8; } vec3 doColor(vec3 p) { return vec3(0.3,0.5,0.8); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv=(fragCoord*2.0-iResolution.xy)/iResolution.y; vec3 ro=vec3(0,3,5); ro.zx*=rotate(iTime*0.1); 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); }