Revision 353f7580
Added by Leszek Koltunski almost 7 years ago
| src/main/java/org/distorted/library/effect/VertexEffectDistort.java | ||
|---|---|---|
| 48 | 48 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 49 | 49 |
// PUBLIC API |
| 50 | 50 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 51 |
// Point (Px,Py) gets moved by vector (Wx,Wy,Wz) where Wx/Wy = Vx/Vy i.e. Wx=aVx and Wy=aVy where |
|
| 51 |
// Def: P-current vertex being distorted, S - center of the effect, X- point where half-line SP meets |
|
| 52 |
// the region circle (if P is inside the circle); (Vx,Vy,Vz) - force vector. |
|
| 53 |
// |
|
| 54 |
// horizontal part |
|
| 55 |
// Point (Px,Py) gets moved by vector (Wx,Wy) where Wx/Wy = Vx/Vy i.e. Wx=aVx and Wy=aVy where |
|
| 52 | 56 |
// a=Py/Sy (N --> when (Px,Py) is above (Sx,Sy)) or a=Px/Sx (W) or a=(w-Px)/(w-Sx) (E) or a=(h-Py)/(h-Sy) (S) |
| 53 | 57 |
// It remains to be computed which of the N,W,E or S case we have: answer: a = min[ Px/Sx , Py/Sy , (w-Px)/(w-Sx) , (h-Py)/(h-Sy) ] |
| 54 | 58 |
// Computations above are valid for screen (0,0)x(w,h) but here we have (-w/2,-h/2)x(w/2,h/2) |
| 55 | 59 |
// |
| 56 |
// the vertical part
|
|
| 57 |
// Let |(v.x,v.y),(ux,uy)| = |PS|, ux-v.x=dx,uy-v.y=dy, f(x) (0<=x<=|SX|) be the shape of the side of the bubble.
|
|
| 58 |
// H(v.x,v.y) = |PS|>|SX| ? 0 : f(|PX|)
|
|
| 59 |
// N(v.x,v.y) = |PS|>|SX| ? (0,0,1) : ( -(dx/|PS|)sin(beta), -(dy/|PS|)sin(beta), cos(beta) ) where tan(beta) is f'(|PX|)
|
|
| 60 |
// ( i.e. normalize( dx, dy, -|PS|/f'(|PX|)) |
|
| 60 |
// vertical part |
|
| 61 |
// Let vector PS = (dx,dy), f(x) (0<=x<=|SX|) be the shape of the side of the bubble.
|
|
| 62 |
// H(Px,Py) = |PS|>|SX| ? 0 : f(|PX|)
|
|
| 63 |
// N(Px,Py) = |PS|>|SX| ? (0,0,1) : ( -(dx/|PS|)sin(beta), -(dy/|PS|)sin(beta), cos(beta) ) where tan(beta) is f'(|PX|)
|
|
| 64 |
// ( i.e. normalize( dx, dy, -|PS|/f'(|PX|) )
|
|
| 61 | 65 |
// |
| 62 |
// Now we also have to take into account the effect horizontal move by V=(u_dVx[i],u_dVy[i]) will have on the normal vector.
|
|
| 66 |
// Now we also have to take into account the effect horizontal move by V=(Vx,Vy) will have on the normal vector.
|
|
| 63 | 67 |
// Solution: |
| 64 | 68 |
// 1. Decompose the V into two subcomponents, one parallel to SX and another perpendicular. |
| 65 | 69 |
// 2. Convince yourself (draw!) that the perpendicular component has no effect on normals. |
| ... | ... | |
| 67 | 71 |
// can be negative depending on the direction) |
| 68 | 72 |
// 4. that in turn leaves the x and y parts of the normal unchanged and multiplies the z component by a! |
| 69 | 73 |
// |
| 70 |
// |Vpar| = (u_dVx[i]*dx - u_dVy[i]*dy) / sqrt(ps_sq) = (Vx*dx-Vy*dy)/ sqrt(ps_sq) (-Vy because y is inverted)
|
|
| 74 |
// |Vpar| = (Vx*dx-Vy*dy) / sqrt(ps_sq) (-Vy because y is inverted)
|
|
| 71 | 75 |
// a = (|SX| - |Vpar|)/|SX| = 1 - |Vpar|/((sqrt(ps_sq)/(1-d)) = 1 - (1-d)*|Vpar|/sqrt(ps_sq) = 1-(1-d)*(Vx*dx-Vy*dy)/ps_sq |
| 72 | 76 |
// |
| 73 | 77 |
// Side of the bubble |
| 74 | 78 |
// |
| 75 | 79 |
// choose from one of the three bubble shapes: the cone, the thin bubble and the thick bubble |
| 76 | 80 |
// Case 1: |
| 77 |
// f(t) = t, i.e. f(x) = uz * x/|SX| (a cone)
|
|
| 78 |
// -|PS|/f'(|PX|) = -|PS|*|SX|/uz but since ps_sq=|PS|^2 and d=|PX|/|SX| then |PS|*|SX| = ps_sq/(1-d)
|
|
| 79 |
// so finally -|PS|/f'(|PX|) = -ps_sq/(uz*(1-d))
|
|
| 81 |
// f(t) = t, i.e. f(x) = Vz * x/|SX| (a cone)
|
|
| 82 |
// -|PS|/f'(|PX|) = -|PS|*|SX|/Vz but since ps_sq=|PS|^2 and d=|PX|/|SX| then |PS|*|SX| = ps_sq/(1-d)
|
|
| 83 |
// so finally -|PS|/f'(|PX|) = -ps_sq/(Vz*(1-d))
|
|
| 80 | 84 |
// |
| 81 | 85 |
// Case 2: |
| 82 | 86 |
// f(t) = 3t^2 - 2t^3 --> f(0)=0, f'(0)=0, f'(1)=0, f(1)=1 (the bell curve) |
| 83 |
// here we have t = x/|SX| which makes f'(|PX|) = 6*uz*|PS|*|PX|/|SX|^3.
|
|
| 84 |
// so -|PS|/f'(|PX|) = (-|SX|^3)/(6uz|PX|) = (-|SX|^2) / (6*uz*d) but
|
|
| 87 |
// here we have t = x/|SX| which makes f'(|PX|) = 6*Vz*|PS|*|PX|/|SX|^3.
|
|
| 88 |
// so -|PS|/f'(|PX|) = (-|SX|^3)/(6Vz|PX|) = (-|SX|^2) / (6*Vz*d) but
|
|
| 85 | 89 |
// d = |PX|/|SX| and ps_sq = |PS|^2 so |SX|^2 = ps_sq/(1-d)^2 |
| 86 |
// so finally -|PS|/f'(|PX|) = -ps_sq/ (6uz*d*(1-d)^2)
|
|
| 90 |
// so finally -|PS|/f'(|PX|) = -ps_sq/ (6Vz*d*(1-d)^2)
|
|
| 87 | 91 |
// |
| 88 | 92 |
// Case 3: |
| 89 | 93 |
// f(t) = 3t^4-8t^3+6t^2 would be better as this satisfies f(0)=0, f'(0)=0, f'(1)=0, f(1)=1, |
| 90 | 94 |
// f(0.5)=0.7 and f'(t)= t(t-1)^2 >=0 for t>=0 so this produces a fuller, thicker bubble! |
| 91 |
// then -|PS|/f'(|PX|) = (-|PS|*|SX)) / (12uz*d*(d-1)^2) but |PS|*|SX| = ps_sq/(1-d) (see above!)
|
|
| 92 |
// so finally -|PS|/f'(|PX|) = -ps_sq/ (12uz*d*(1-d)^3)
|
|
| 95 |
// then -|PS|/f'(|PX|) = (-|PS|*|SX)) / (12Vz*d*(d-1)^2) but |PS|*|SX| = ps_sq/(1-d) (see above!)
|
|
| 96 |
// so finally -|PS|/f'(|PX|) = -ps_sq/ (12Vz*d*(1-d)^3)
|
|
| 93 | 97 |
// |
| 94 | 98 |
// Now, new requirement: we have to be able to add up normal vectors, i.e. distort already distorted surfaces. |
| 95 | 99 |
// If a surface is given by z = f(x,y), then the normal vector at (x0,y0) is given by (-df/dx (x0,y0), -df/dy (x0,y0), 1 ). |
| ... | ... | |
| 97 | 101 |
// then the normal to g = f1+f2 is simply given by (f1x+f2x,f1y+f2y,1), i.e. if the third components are equal, then we |
| 98 | 102 |
// can simply add up the first and second components. |
| 99 | 103 |
// |
| 100 |
// Thus we actually want to compute N(v.x,v.y) = a*(-(dx/|PS|)*f'(|PX|), -(dy/|PS|)*f'(|PX|), 1) and keep adding
|
|
| 104 |
// Thus we actually want to compute N(Px,Py) = a*(-(dx/|PS|)*f'(|PX|), -(dy/|PS|)*f'(|PX|), 1) and keep adding
|
|
| 101 | 105 |
// the first two components. (a is the horizontal part) |
| 106 |
// |
|
| 107 |
// 2019-01-09 fixes for stuff discovered with the Earth testing app |
|
| 108 |
// Now, the above stuff works only if the normal vector is close to (0,0,1). To make it work for any situation, |
|
| 109 |
// rotate the normal, force and ps vectors along the axis (n.y,-n.x,0) and angle A between the normal and (0,0,1) |
|
| 110 |
// then modify the rotated normal (so (0,0,1)) and rotate the modified normal back. |
|
| 111 |
// |
|
| 112 |
// if we have a vector (vx,vy,vz) that w want to rotate with a rotation that turns the normal into (0,0,1), then |
|
| 113 |
// a) axis of rotation = (n.x,n.y,n.z) x (0,0,1) = (n.y,-n.x,0) |
|
| 114 |
// b) angle of rotation A: cosA = (n.x,n.y,n.z)*(0,0,1) = n.z (and sinA = + sqrt(1-n.z^2)) |
|
| 115 |
// |
|
| 116 |
// so normalized axisNor = (n.x/ sinA, -n.y/sinA, 0) and now from Rodrigues rotation formula |
|
| 117 |
// Vrot = v*cosA + (axisNor x v)*sinA + axisNor*(axis*v)*(1-cosA) |
|
| 118 |
// |
|
| 119 |
// which makes Vrot = (a+n.y*c , b-n.y*c , v*n) where |
|
| 120 |
// a = vx*nz-vz*nx , b = vy*nz-vz*ny , c = (vx*ny-vy*nx)/(1+nz) (unless n=(0,0,-1)) |
|
| 102 | 121 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 103 | 122 |
/** |
| 104 | 123 |
* Have to call this before the shaders get compiled (i.e before Distorted.onCreate()) for the Effect to work. |
| ... | ... | |
| 107 | 126 |
{
|
| 108 | 127 |
addEffect(EffectName.DISTORT, |
| 109 | 128 |
|
| 110 |
"vec3 center = vUniforms[effect+1].yzw; \n" |
|
| 111 |
+ "vec3 ps = center-v; \n" |
|
| 112 |
+ "vec3 force = vUniforms[effect].xyz; \n" |
|
| 113 |
+ "float d = degree(vUniforms[effect+2],center,ps); \n" |
|
| 114 |
+ "float denom = dot(ps+(1.0-d)*force,ps); \n" |
|
| 115 |
+ "float one_over_denom = 1.0/(denom-0.001*(sign(denom)-1.0)); \n" // = denom==0 ? 1000:1/denom; |
|
| 129 |
"vec3 center = vUniforms[effect+1].yzw; \n" |
|
| 130 |
+ "vec3 ps = center-v; \n" |
|
| 131 |
+ "vec3 force = vUniforms[effect].xyz; \n" |
|
| 132 |
+ "vec4 region= vUniforms[effect+2]; \n" |
|
| 133 |
+ "float d = degree(region,center,ps); \n" |
|
| 134 |
|
|
| 135 |
+ "if( d>0.0 ) \n" |
|
| 136 |
+ " { \n"
|
|
| 137 |
+ " v += d*force; \n" |
|
| 116 | 138 |
|
| 117 |
//v.z += force.z*d; // cone
|
|
| 118 |
//b = -(force.z*(1.0-d))*one_over_denom; //
|
|
| 139 |
+ " float tp = 1.0+n.z; \n"
|
|
| 140 |
+ " float tr = 1.0 / (tp - (1.0 - sign(tp))); \n"
|
|
| 119 | 141 |
|
| 120 |
//v.z += force.z*d*d*(3.0-2.0*d); // thin bubble |
|
| 121 |
//b = -(6.0*force.z*d*(1.0-d)*(1.0-d))*one_over_denom; // |
|
| 142 |
+ " float ap = ps.x*n.z - ps.z*n.x; \n" // likewise rotate the ps vector |
|
| 143 |
+ " float bp = ps.y*n.z - ps.z*n.y; \n" // |
|
| 144 |
+ " float cp =(ps.x*n.y - ps.y*n.x)*tr; \n" // |
|
| 145 |
+ " vec3 psRot = vec3( ap+n.y*cp , bp-n.x*cp , dot(ps,n) ); \n" // |
|
| 122 | 146 |
|
| 123 |
+ "v.z += force.z*d*d*(3.0*d*d -8.0*d +6.0); \n" // thick bubble |
|
| 124 |
+ "float b = -(12.0*force.z*d*(1.0-d)*(1.0-d)*(1.0-d))*one_over_denom; \n" // |
|
| 147 |
+ " psRot = normalize(psRot); \n" |
|
| 148 |
+ " vec3 N = vec3( -dot(force,n)*psRot.xy, region.w ); \n" // modified rotated normal |
|
| 149 |
// dot(force,n) is rotated force.z |
|
| 150 |
+ " float an = N.x*n.z + N.z*n.x; \n" // now create the normal vector |
|
| 151 |
+ " float bn = N.y*n.z + N.z*n.y; \n" // back from our modified normal |
|
| 152 |
+ " float cn =(N.x*n.y - N.y*n.x)*tr; \n" // rotated back |
|
| 153 |
+ " n = vec3( an+n.y*cn , bn-n.x*cn , -N.x*n.x-N.y*n.y+N.z*n.z);\n" // notice 4 signs change! |
|
| 125 | 154 |
|
| 126 |
+ "v.xy += d*force.xy; \n"
|
|
| 127 |
+ "n.xy += n.z*b*ps.xy;"
|
|
| 155 |
+ " n = normalize(n); \n"
|
|
| 156 |
+ " } \n"
|
|
| 128 | 157 |
); |
| 129 | 158 |
} |
| 130 | 159 |
|
| src/main/java/org/distorted/library/effect/VertexEffectSwirl.java | ||
|---|---|---|
| 70 | 70 |
{
|
| 71 | 71 |
addEffect(EffectName.SWIRL, |
| 72 | 72 |
|
| 73 |
"vec3 center = vUniforms[effect+1].yzw; \n" |
|
| 74 |
+ "vec3 PS = center-v.xyz; \n" |
|
| 75 |
+ "vec4 SO = vUniforms[effect+2]; \n" |
|
| 76 |
+ "float d1_circle = degree_region(SO,PS); \n" |
|
| 77 |
+ "float d1_bitmap = degree_bitmap(center,PS); \n" |
|
| 78 |
|
|
| 79 |
+ "float alpha = vUniforms[effect].x; \n" |
|
| 80 |
+ "float sinA = sin(alpha); \n" |
|
| 81 |
+ "float cosA = cos(alpha); \n" |
|
| 73 |
"vec3 center = vUniforms[effect+1].yzw; \n" |
|
| 74 |
+ "vec3 PS = center-v.xyz; \n" |
|
| 75 |
+ "vec4 SO = vUniforms[effect+2]; \n" |
|
| 76 |
+ "float d1_circle = degree_region(SO,PS); \n" |
|
| 77 |
+ "float d1_object = degree_object(center,PS); \n" |
|
| 78 |
+ "float alpha = vUniforms[effect].x; \n" |
|
| 79 |
+ "float sinA = sin(alpha); \n" |
|
| 80 |
+ "float cosA = cos(alpha); \n" |
|
| 82 | 81 |
|
| 83 | 82 |
+ "vec3 PS2 = vec3( PS.x*cosA+PS.y*sinA,-PS.x*sinA+PS.y*cosA, PS.z ); \n" // vector PS rotated by A radians clockwise around center. |
| 84 |
+ "vec4 SG = (1.0-d1_circle)*SO; \n" // coordinates of the dilated circle P is going to get rotated around
|
|
| 85 |
+ "float d2 = max(0.0,degree(SG,center,PS2)); \n" // make it a max(0,deg) because otherwise when center=left edge of the
|
|
| 86 |
// bitmap some points end up with d2<0 and they disappear off view.
|
|
| 87 |
+ "v.xy += min(d1_circle,d1_bitmap)*(PS.xy - PS2.xy/(1.0-d2)); \n" // if d2=1 (i.e P=center) we should have P unchanged. How to do it?
|
|
| 83 |
+ "vec4 SG = (1.0-d1_circle)*SO; \n" // coordinates of the dilated circle P is going to get rotated around
|
|
| 84 |
+ "float d2 = max(0.0,degree(SG,center,PS2)); \n" // make it a max(0,deg) because otherwise when center=left edge of the
|
|
| 85 |
// object some points end up with d2<0 and they disappear off view.
|
|
| 86 |
+ "v.xy += min(d1_circle,d1_object)*(PS.xy - PS2.xy/(1.0-d2)); \n" // if d2=1 (i.e P=center) we should have P unchanged. How to do it?
|
|
| 88 | 87 |
); |
| 89 | 88 |
} |
| 90 | 89 |
|
| src/main/res/raw/main_vertex_shader.glsl | ||
|---|---|---|
| 59 | 59 |
// We still have to avoid division by 0 when p.x = +- u_objD.x or p.y = +- u_objD.y (i.e on the edge of the Object) |
| 60 | 60 |
// We do that by first multiplying the above 'float d' with sign(denominator1*denominator2)^2. |
| 61 | 61 |
// |
| 62 |
// 2019-01-09: make this 3D. The trick: we want only the EDGES of the cuboid to stay constant. |
|
| 63 |
// the interiors of the Faces move! Thus, we want the MIDDLE of the PS/(sign(PS)*u_objD+S) ! |
|
| 62 | 64 |
////////////////////////////////////////////////////////////////////////////////////////////// |
| 63 |
// return degree of the point as defined by the bitmap rectangle
|
|
| 65 |
// return degree of the point as defined by the object cuboid (u_objD.x X u_objD.y X u_objD.z)
|
|
| 64 | 66 |
|
| 65 |
float degree_bitmap(in vec3 S, in vec3 PS)
|
|
| 67 |
float degree_object(in vec3 S, in vec3 PS)
|
|
| 66 | 68 |
{
|
| 69 |
vec3 ONE = vec3(1.0,1.0,1.0); |
|
| 67 | 70 |
vec3 A = sign(PS)*u_objD + S; |
| 68 | 71 |
|
| 69 |
vec3 signA = sign(A); //
|
|
| 70 |
vec3 signA_SQ = signA*signA; // div = PS/A if A!=0, 0 otherwise.
|
|
| 71 |
vec3 div = signA_SQ*PS/(A-(vec3(1.0,1.0,1.0)-signA_SQ)); //
|
|
| 72 |
vec3 signA = sign(A); // |
|
| 73 |
vec3 signA_SQ = signA*signA; // div = PS/A if A!=0, 0 otherwise. |
|
| 74 |
vec3 div = signA_SQ*PS/(A-(ONE-signA_SQ)); //
|
|
| 72 | 75 |
|
| 73 |
return 1.0-max(div.x,div.y); |
|
| 76 |
float d1= div.x-div.y; |
|
| 77 |
float d2= div.y-div.z; |
|
| 78 |
float d3= div.x-div.z; |
|
| 79 |
|
|
| 80 |
if( d1*d2>0.0 ) return 1.0-div.y; // |
|
| 81 |
if( d1*d3>0.0 ) return 1.0-div.z; // return 1-middle(div.x,div.y,div.z) |
|
| 82 |
return 1.0-div.x; // |
|
| 74 | 83 |
} |
| 75 | 84 |
|
| 76 | 85 |
////////////////////////////////////////////////////////////////////////////////////////////// |
| 77 |
// Return degree of the point as defined by the Region. Currently only supports circular regions.
|
|
| 86 |
// Return degree of the point as defined by the Region. Currently only supports spherical regions.
|
|
| 78 | 87 |
// |
| 79 |
// Let us first introduce some notation. |
|
| 80 | 88 |
// Let 'PS' be the vector from point P (the current vertex) to point S (the center of the effect). |
| 81 |
// Let region.xy be the vector from point S to point O (the center point of the region circle)
|
|
| 82 |
// Let region.z be the radius of the region circle.
|
|
| 83 |
// (This all should work regardless if S is inside or outside of the circle).
|
|
| 89 |
// Let region.xyz be the vector from point S to point O (the center point of the region sphere)
|
|
| 90 |
// Let region.w be the radius of the region sphere.
|
|
| 91 |
// (This all should work regardless if S is inside or outside of the sphere).
|
|
| 84 | 92 |
// |
| 85 |
// Then, the degree of a point with respect to a given (circular!) Region is defined by:
|
|
| 93 |
// Then, the degree of a point with respect to a given (spherical!) Region is defined by:
|
|
| 86 | 94 |
// |
| 87 |
// If P is outside the circle, return 0.
|
|
| 88 |
// Otherwise, let X be the point where the halfline SP meets the region circle - then return |PX|/||SX|,
|
|
| 95 |
// If P is outside the sphere, return 0.
|
|
| 96 |
// Otherwise, let X be the point where the halfline SP meets the sphere - then return |PX|/||SX|,
|
|
| 89 | 97 |
// aka the 'degree' of point P. |
| 90 | 98 |
// |
| 91 | 99 |
// We solve the triangle OPX. |
| ... | ... | |
| 117 | 125 |
} |
| 118 | 126 |
|
| 119 | 127 |
////////////////////////////////////////////////////////////////////////////////////////////// |
| 120 |
// return min(degree_bitmap,degree_region). Just like degree_region, currently only supports circles.
|
|
| 128 |
// return min(degree_object,degree_region). Just like degree_region, currently only supports spheres.
|
|
| 121 | 129 |
|
| 122 | 130 |
float degree(in vec4 region, in vec3 S, in vec3 PS) |
| 123 | 131 |
{
|
| ... | ... | |
| 130 | 138 |
vec3 signA = sign(A); |
| 131 | 139 |
vec3 signA_SQ = signA*signA; |
| 132 | 140 |
vec3 div = signA_SQ*PS/(A-(vec3(1.0,1.0,1.0)-signA_SQ)); |
| 133 |
float E = 1.0-max(div.x,div.y); |
|
| 141 |
|
|
| 142 |
float d1= div.x-div.y; |
|
| 143 |
float d2= div.y-div.z; |
|
| 144 |
float d3= div.x-div.z; |
|
| 145 |
float E; |
|
| 146 |
|
|
| 147 |
if( d1*d2>0.0 ) E= 1.0-div.y; |
|
| 148 |
else if( d1*d3>0.0 ) E= 1.0-div.z; |
|
| 149 |
else E= 1.0-div.x; |
|
| 134 | 150 |
|
| 135 | 151 |
float ps_sq = dot(PS,PS); |
| 136 | 152 |
float one_over_ps_sq = 1.0/(ps_sq-(sign(ps_sq)-1.0)); // return 1.0 if ps_sq = 0.0 |
Also available in: Unified diff
Make Distort truly 3D.