Edited at

glsl 2D座標変換 疑似パースペクティブまとめ


概要

良く使われている疑似パースペクティブですが、glsl特有の省略によって、良く使われている疑似パースペクティブは座標がひっくり返っていたりして、意味あるテクスチャーを貼る場合に困ったので、自分用の覚書です。岩の模様とか意味がないやつは別に問題ないです。


画像変換前の画像

image.png


Bottom

image.png


Top

image.png


Left

image.png


Right

image.png


水平

image.png


垂直

image.png


水平 + 垂直

image.png

vec2 fakePerspectiveVH(vec2 p){

float r = 1./abs(abs(p.x)<abs(p.y)?p.y:p.x);
return abs(p.x)<abs(p.y)?vec2(p.x*r,(-2.*(r-1.)+1.)*sign(p.y)):vec2((-2.*(r-1.)+1.)*sign(p.x),p.y*r);
}


その他

image.png

キューブの中を再現

  l+=mapCheck(fakePerspectiveVH(p));

l+=mapCheck(p*2.);


サンプルプログラム

precision mediump float;

uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene

const float PI = acos(-1.);
const float TAU = PI * 2.;

#define saturate(x) clamp(x,0.,1.)
#define _tail2x(p,n) (mod(p,2.)-1.)

vec2 tail2x(vec2 p,float n){p*=n;return _tail2x(p,n);}
vec2 tailX2x(vec2 p,float n){p*=n;return vec2(_tail2x(p.x,n),p.y);}
vec2 tailY2x(vec2 p,float n){p*=n;return vec2(p.x,_tail2x(p.y,n));}

float cross(vec2 v1, vec2 v2) {return dot(v1,vec2(v2.y,-v2.x));}
mat2 rot(float a){float c=cos(a),s=sin(a);return mat2(c,-s,s,c);}

// signed distance
float sd(float d,float r){return r-d;}
float sd(float d){return 1.-d;}

float fill_na(float d){return step(0.,d);}
float fill(float d){return smoothstep(0.,0.01,d);}
float stroke(float d,float w){return 1.-smoothstep(w,w+0.01,abs(d));}
float strokeInner(float d,float w){return stroke(d-w,w);}
float strokeOuter(float d,float w){return stroke(d+w,w);}

float maxabs(vec2 p){return max(abs(p.x), abs(p.y));}

float sLocater(vec2 p){
float l=stroke(sd(maxabs(tailX2x(p,8.))),.016)*stroke(p.y,.03);
l+=stroke(sd(maxabs(tailY2x(p,8.))),.016)*stroke(p.x,.03);
l+=stroke(p.x,.001);
l+=stroke(p.y,.001);
return l;
}

vec2 fakePerspectiveBottom (vec2 p){
float r = 1./p.y;
return vec2(-p.x*r, -2.*(r+1.)-1.);
}

vec2 fakePerspectiveTop (vec2 p){
float r = 1./p.y;
return vec2(p.x*r, -2.*(r-1.)+1.);
}

vec2 fakePerspectiveLeft(vec2 p){
float r = 1./p.x;
return vec2(-2.*(r+1.)-1.,-p.y*r);
}

vec2 fakePerspectiveRight(vec2 p){
float r = 1./p.x;
return vec2(-2.*(r-1.)+1.,p.y*r);
}

vec2 fakePerspectiveVertical(vec2 p){
float r = 1./abs(p.x);
return vec2((-2.*(r-1.)+1.)*sign(p.x),p.y*r);
}

vec2 fakePerspectiveHorizontal(vec2 p){
float r = 1./abs(p.y);
return vec2(p.x*r,(-2.*(r-1.)+1.)*sign(p.y));
}

vec2 fakePerspectiveVH(vec2 p){
float r = 1./abs(abs(p.x)<abs(p.y)?p.y:p.x);
return abs(p.x)<abs(p.y)?vec2(p.x*r,(-2.*(r-1.)+1.)*sign(p.y)):vec2((-2.*(r-1.)+1.)*sign(p.x),p.y*r);
}

float pCheckers(vec2 p,float n){vec2 q=p*n;return mod(floor(q.x)+floor(q.y),2.0);}

float seg(vec2 p,float s,float k){p=abs(p*k);return max(s+p.x,s*.5+p.y+p.x);}
float seg7(vec2 p,int n,float s,float k){
float l=5.,w=s*.5;
vec2 q=p.yx,h=vec2(s,0.),v=vec2(-w,w);
l = (n!=1 && n!=4 ?min(seg(q-h,s,k),l):l);
l = (n!=1 && n!=2 && n!=3 && n!=7?min(seg(p-v,s,k),l):l);
l = (n!=5 && n!=6 ?min(seg(p-w,s,k),l):l);
l = (n!=0 && n!=1 && n!=7 ?min(seg(q ,s,k),l):l);
l = (n==0 || n==2 || n==6 || n==8?min(seg(p+w,s,k),l):l);
l = (n!=2 ?min(seg(p+v,s,k),l):l);
l = (n!=1 && n!=4 && n!=7 ?min(seg(q+h,s,k),l):l);
return l;
}

float mapCheck(vec2 p){
float l=0.;
vec2 q=tail2x(p,10.);
l+= pCheckers(p,5.)*.25+.25;;
l+= stroke(seg7(q-vec2(.3,0.),int(floor((p.x+1.)*5.)),.4,1.3),.5);
l+= stroke(seg7(q+vec2(.3,0.),int(floor((1.-p.y)*5.)),.4,1.3),.5);
l+= strokeInner(sd(length(p)),.01);
l*=step(-1.,p.x)*step(p.x,1.)*step(-1.,p.y)*step(p.y,1.);
return l;
}

void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
vec2 P =p;
float l=0.;
l+=mapCheck(fakePerspectiveHorizontal(p));
//l+=mapCheck(fakePerspectiveVertical(p));
//l+=mapCheck(fakePerspectiveVH(p));
//l+=mapCheck(p*2.);

//l=mapCheck(fakePerspectiveBottom(p));
//l=mapCheck(fakePerspectiveTop(p));
//l=mapCheck(fakePerspectiveLeft(p));
//l=mapCheck(fakePerspectiveRight(p));

l+=sLocater(p);

gl_FragColor = vec4(vec3(l), 1.0);
}