tools/lotus-study/

shaders.js

export const petalVertex = /* glsl */`
  uniform float uPhase, uZoom, uBloom, uCurl;
  attribute float aIndex, aAngle, aRing, aLength, aWidth;
  varying vec2 vUv;
  varying vec3 vNormal;
  varying float vOpening, vFade, vRing;
  const float PI = 3.141592653589793;
  vec3 petal(float u, float v, float opening) {
    float stem = mix(.38,.15,aRing*.5);
    float spread = mix(.27,1.,opening);
    float radius = stem + aLength * u * spread;
    float width = aWidth * pow(max(sin(PI*u),.00001),.78) * (1.-.21*u) * mix(.48,1.,opening);
    float side = v*width;
    float cup = uCurl*(.23*sin(PI*u) + .17*v*v*sin(PI*u));
    float height = (1.-opening)*aLength*.65*u + cup - .09*opening*u*u*u;
    float wave = .012*sin(u*PI*2.+aAngle*3.)*sin(PI*u)*v;
    return vec3(radius,side,height+wave);
  }
  void main() {
    vUv=uv;
    float age=aIndex+uPhase;
    float opening=smoothstep(-1.7,.65,age+.65-aRing*.34+uBloom);
    vOpening=opening;vRing=aRing;
    float s=pow(uZoom,age);
    vFade=smoothstep(.002,.008,s);
    float u=uv.x, v=uv.y*2.-1.;
    vec3 p=petal(u,v,opening);
    vec3 tangent=petal(min(u+.001,1.),v,opening)-petal(max(u-.001,0.),v,opening);
    vec3 across=petal(u,min(v+.001,1.),opening)-petal(u,max(v-.001,-1.),opening);
    vec3 normal=normalize(cross(tangent,across));
    mat2 turn=mat2(cos(aAngle),sin(aAngle),-sin(aAngle),cos(aAngle));
    p.xy=turn*p.xy*s;
    normal.xy=turn*normal.xy;
    // Orthographic travel keeps the foreground and paper independent of the zoom.
    p.z=p.z*.17+aRing*.055-aIndex*.28;
    vNormal=normal;
    gl_Position=projectionMatrix*modelViewMatrix*vec4(p,1.);
  }
`;

export const petalFragment = /* glsl */`
  uniform float uGrain,uContrast,uPhase;
  uniform vec3 uPaper;
  varying vec2 vUv;
  varying vec3 vNormal;
  varying float vOpening,vFade,vRing;
  float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123);}
  void main(){
    float side=abs(vUv.y*2.-1.);
    float coverage=(1.-smoothstep(.945,1.,side))*vFade;
    if(hash(gl_FragCoord.xy) > coverage) discard;
    vec3 n=normalize(vNormal);
    float shade=1.-max(0.,dot(n,normalize(vec3(-.45,.35,1.))));
    float edge=pow(side,10.)*.13;
    float crease=exp(-pow((vUv.y-.5)*26.,2.))*.025;
    float root=(1.-smoothstep(.02,.32,vUv.x))*.035;
    float grainA=hash(gl_FragCoord.xy);
    float grainB=hash(gl_FragCoord.xy+71.37);
    float grain=mix(grainA,grainB,.5+.5*sin(uPhase*6.2831853));
    float vein=sin(vUv.y*190.+sin(vUv.x*10.)*.7)*.0035;
    float ink=(.035+shade*.26+edge+crease+root+vein)*(.5+uContrast*1.6);
    ink+=(grain-.5)*uGrain*.14;
    ink=clamp(ink,.005,.52);
    vec3 color=mix(uPaper,vec3(.31,.34,.30),ink);
    gl_FragColor=vec4(color,1.);
  }
`;

export const paperVertex = /* glsl */`
  varying vec2 vUv;
  void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}
`;
export const paperFragment = /* glsl */`
  uniform vec3 uPaper;
  uniform float uGrain,uPhase;
  varying vec2 vUv;
  float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123);}
  void main(){
    float tooth=hash(gl_FragCoord.xy)-.5;
    float fine=hash(gl_FragCoord.xy+100.1)-.5;
    float live=mix(tooth,fine,.12+.08*sin(uPhase*6.2831853));
    float vignette=length((vUv-.5)*vec2(1.,.75));
    gl_FragColor=vec4(uPaper+live*uGrain*.038-vignette*.007,1.);
  }
`;