commit 73af528565298bb0a546f98d570169a250336054
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Wed Oct 12 01:29:28 2016 +0100

    Now we can add up the WAVE effect to others with smooth shading! Remaining issues:
    
    - when angle A < 0, the shades are wrong
    - sometimes (check with the 3D vertex & Fragment effects' app) we get black spots at seemingly random points. Looks like computational instability again.

diff --git a/src/main/res/raw/main_vertex_shader.glsl b/src/main/res/raw/main_vertex_shader.glsl
index e2df98e..8d29a22 100644
--- a/src/main/res/raw/main_vertex_shader.glsl
+++ b/src/main/res/raw/main_vertex_shader.glsl
@@ -78,13 +78,16 @@ float degree_bitmap(in vec2 S, in vec2 PS)
 //////////////////////////////////////////////////////////////////////////////////////////////
 // Return degree of the point as defined by the Region. Currently only supports circular regions.
 //
+// Let us first introduce some notation.
 // Let 'PS' be the vector from point P (the current vertex) to point S (the center of the effect).
-// Should work regardless if S is inside or outside of the circle.
 // Let region.xy be the vector from point S to point O (the center point of the region circle)
 // Let region.z be the radius of the region circle.
+// (This all should work regardless if S is inside or outside of the circle).
+//
+// Then, the degree of a point with respect to a given (circular!) Region is defined by:
 //
 // If P is outside the circle, return 0.
-// Otherwise, let X be the point where the halfline SP meets the region circle - return |PX|/||SX|,
+// Otherwise, let X be the point where the halfline SP meets the region circle - then return |PX|/||SX|,
 // aka the 'degree' of point P.
 //
 // We solve the triangle OPX.
@@ -267,16 +270,16 @@ void deform(in int effect, inout vec4 v)
 // so finally -|PS|/f'(|PX|) = -ps_sq/ (6uz*d*(1-d)^2)
 //                  
 // Case 3:
-// f(t) = 3t^4-8t^3+6t^2 would be better as this safisfies f(0)=0, f'(0)=0, f'(1)=0, f(1)=1,
+// 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,
 // f(0.5)=0.7 and f'(t)= t(t-1)^2 >=0 for t>=0 so this produces a fuller, thicker bubble!
 // then -|PS|/f'(|PX|) = (-|PS|*|SX)) / (12uz*d*(d-1)^2) but |PS|*|SX| = ps_sq/(1-d) (see above!) 
 // so finally -|PS|/f'(|PX|) = -ps_sq/ (12uz*d*(1-d)^3)  
 //
 // Now, new requirement: we have to be able to add up normal vectors, i.e. distort already distorted surfaces.
-// 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 ).
+// 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 ).
 // so if we have two surfaces defined by f1(x,y) and f2(x,y) with their normals expressed as (f1x,f1y,1) and (f2x,f2y,1) 
-// then the normal to g = f1+f2 is simply given by (f1x+f2x,f1y+f2y,1), i.e. if the third component is 1, then we can simply
-// add up the first and second components.
+// 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
+// can simply add up the first and second components.
 //
 // Thus we actually want to compute N(v.x,v.y) = a*(-(dx/|PS|)*f'(|PX|), -(dy/|PS|)*f'(|PX|), 1) and keep adding
 // the first two components. (a is the horizontal part)
@@ -346,6 +349,51 @@ void swirl(in int effect, inout vec4 v)
 // WAVE EFFECT
 //
 // Directional sinusoidal wave effect.
+//
+// This is an effect from a (hopefully!) generic family of effects of the form (vec3 V: |V|=1 , f(x,y) )  (*)
+// i.e. effects defined by a unit vector and an arbitrary function. Those effects are defined to move each
+// point (x,y,0) of the XY plane to the point (x,y,0) + V*f(x,y).
+//
+// In this case V is defined by angles A and B (sines and cosines of which are precomputed in
+// EffectQueueVertex and passed in the uniforms).
+// Let's move V to start at the origin O, let point C be the endpoint of V, and let C' be C's projection
+// to the XY plane. Then A is defined to be the angle C0C' and angle B is the angle C'O(axisY).
+//
+// Also, in this case f(x,y) = amplitude*sin(x/length), with those 2 parameters passed in uniforms.
+//
+////////////////////////////////////////////////////////
+// How to compute any generic effect of type (*)
+////////////////////////////////////////////////////////
+//
+// By definition, the vertices move by f(x,y)*V.
+//
+// Normals are much more complicated.
+// Let angle X be the angle (0,Vy,Vz)((0,Vy,0)(Vx,Vy,Vz).
+// Let angle Y be the angle (Vx,0,Vz)((Vx,0,0)(Vx,Vy,Vz).
+//
+// Then it can be shown that the resulting surface, at point to which point (x0,y0,0) got moved to,
+// has 2 tangent vectors given by
+//
+// SX = (1.0+cosX*fx , cosY*sinX*fx , sinY*sinX*fx);  (**)
+// SY = (cosX*sinY*fy , 1.0+cosY*fy , sinX*sinY*fy);  (***)
+//
+// and then obviously the normal N is given by N= SX x SY .
+//
+// We still need to remember the note from the distort function about adding up normals:
+// we first need to 'normalize' the normals to make their third components equal, and then we
+// simply add up the first and the second component while leaving the third unchanged.
+//
+// How to see facts (**) and (***) ? Briefly:
+// a) compute the 2D analogon and conclude that in this case the tangent SX is given by
+//    SX = ( cosA*f'(x) +1, sinA*f'(x) )    (where A is the angle vector V makes with X axis )
+// b) cut the resulting surface with plane P which
+//    - includes vector V
+//    - crosses plane XY along line parallel to X axis
+// c) apply the 2D analogon and notice that the tangent vector to the curve that is the common part of P
+//    and our surface (I am talking about the tangent vector which belongs to P) is given by
+//    (1+cosX*fx,0,sinX*fx) rotated by angle Y (where angles X,Y are defined above) along vector (1,0,0).
+// d) compute the above and see that this is equal precisely to SX from (**).
+// e) repeat points b,c,d in direction Y and come up with (***).
 
 void wave(in int effect, inout vec4 v, inout vec4 n)
   {
@@ -363,32 +411,35 @@ void wave(in int effect, inout vec4 v, inout vec4 n)
     float sinB = vUniforms[effect  ].w;
     float cosB = vUniforms[effect+1].y;
 
-    float angle= 1.578*(-ps.x*cosB-ps.y*sinB) / length;  // -ps.x and -ps.y becuase the 'ps=center-v.xy' inverts the XY axis!
+    float angle= 1.578*(-ps.x*cosB-ps.y*sinB) / length;  // -ps.x and -ps.y because the 'ps=center-v.xy' inverts the XY axis!
     vec3 dir   = vec3(sinB*cosA,cosB*cosA,sinA);
 
     v.xyz += sin(angle)*deg*dir;
 
-    float sqrtX = sqrt(dir.y*dir.y + dir.z*dir.z);
-    float sqrtY = sqrt(dir.x*dir.x + dir.z*dir.z);
+    if( n.z != 0.0 )
+      {
+      float sqrtX = sqrt(dir.y*dir.y + dir.z*dir.z);
+      float sqrtY = sqrt(dir.x*dir.x + dir.z*dir.z);
 
-    float sinX = ( sqrtY==0.0 ? 0.0 : dir.z / sqrtY);
-    float cosX = ( sqrtY==0.0 ? 1.0 : dir.x / sqrtY);
-    float sinY = ( sqrtX==0.0 ? 0.0 : dir.z / sqrtX);
-    float cosY = ( sqrtX==0.0 ? 1.0 : dir.y / sqrtX);
+      float sinX = ( sqrtY==0.0 ? 0.0 : dir.z / sqrtY);
+      float cosX = ( sqrtY==0.0 ? 1.0 : dir.x / sqrtY);
+      float sinY = ( sqrtX==0.0 ? 0.0 : dir.z / sqrtX);
+      float cosY = ( sqrtX==0.0 ? 1.0 : dir.y / sqrtX);
 
-    float tmp = 1.578*cos(angle)*deg/length;
+      float tmp = 1.578*cos(angle)*deg/length;
 
-    float fx = cosB*tmp;
-    float fy = sinB*tmp;
+      float fx = cosB*tmp;
+      float fy = sinB*tmp;
 
-    vec3 sx = vec3 (1.0+cosX*fx,cosY*sinX*fx,sinY*sinX*fx);
-    vec3 sy = vec3 (cosX*sinY*fy,1.0+cosY*fy,sinX*sinY*fy);
+      vec3 sx = vec3 (1.0+cosX*fx,cosY*sinX*fx,sinY*sinX*fx);
+      vec3 sy = vec3 (cosX*sinY*fy,1.0+cosY*fy,sinX*sinY*fy);
 
-    vec3 normal = cross(sx,sy);
+      vec3 normal = cross(sx,sy);
 
-    if( n.z != 0.0 )
-      {
-      n.xyz = n.z*normal;
+      if( normal.z > 0.0 )
+        {
+        n.xy += (n.z/normal.z)*vec2(normal.x,normal.y);
+        }
       }
     }
   }
