precision highp float; varying vec2 v_TexCoords; uniform sampler2D u_Texture; uniform sampler2D u_Segmentation; uniform sampler2D u_InputMask; uniform sampler2D u_Gradient; uniform sampler2D u_Lut; uniform vec2 u_TextureSize; uniform vec2 u_RenderSize; uniform float u_Time; uniform mat4 u_STMatrix; uniform mat4 u_STMatrix1; // Tweakable uniforms uniform float uThickness; uniform float uInnerScale; uniform float uInnerScaleChange; uniform float uOuterScale; uniform float uOuterScaleChange; uniform float uSaturation; uniform float uBrightness; uniform float uContrast; uniform float uShapeBrightness; uniform float uRotationSpeed; uniform float uRotationRange; uniform float uGradientStretch; // uniforms from our script uniform float uFrameX; uniform float uFrameY; uniform float uHeadSize; uniform float uFrameScale; uniform float uFrameOffsetY; uniform float uFlickerProgress; //face uniforms uniform float faceCenterX; uniform float faceCenterY; uniform float faceWidth; uniform float faceHeight; #define TWO_PI 6.2831853071795864769252867665590 float mirror(in float x) { float f = fract(x); float m = floor(mod(x, 2.)); float fm = f * m; return f + m - fm * 2.; } vec2 mirror(in vec2 xy) { vec2 f = fract(xy); vec2 m = floor(mod(xy, 2.)); vec2 fm = f * m; return f + m - fm * 2.; } float gridSDF(in vec2 st) { vec2 grid = mirror(st); return max(grid.x, grid.y); } vec2 cartesian2polar(in vec2 st) { st = st * 2. - 1.; return vec2(length(st), atan(st.y, st.x)); } float luma(vec3 v) { return dot(v, vec3(0.2989, 0.5870, 0.1140)); } float ramp(float edge0, float edge1, float x) { return clamp( (x - edge0) / (edge1 - edge0), 0.0, 1.0 ); } vec2 rotate(vec2 v, float a) { float s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c); return m * v; } vec2 rotateUV(vec2 uv, float rotation, vec2 mid) { return vec2( cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x, cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y ); } vec2 transformTexCoords(in mat4 transform, in vec2 v) { vec4 tmp = transform * vec4( v, 0., 1. ); return tmp.st / tmp.q; } mat4 rotate4d(in vec3 axis, in float radians) { axis = normalize(axis); float s = sin(radians); float c = cos(radians); float oc = 1.0 - c; return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 0.0, 0.0, 0.0, 1.0); } mat4 rotate4dY(in float theta){ return mat4( vec4(cos(theta),0.,-sin(theta),0), vec4(0.,1.,0.,0.), vec4(sin(theta),0.,cos(theta),0.), vec4(0.,0.,0.,1.)); } vec3 rotateY(in vec3 pos, in float radian, in vec3 center) { return (rotate4dY(radian) * vec4((pos - center), 1.)).xyz + center; } vec3 rotateY(in vec3 pos, in float radian) { #ifdef CENTER_3D return rotateY(pos, radian, CENTER_3D); #else return rotateY(pos, radian, vec3(.0)); #endif } // Color transformation and Blending Functions // ------------------------------------------- vec3 desaturate(vec3 color, float amount) { vec3 luma = vec3(0.3, 0.59, 0.11); vec3 gray = vec3(dot(luma, color)); return mix(color, gray, amount); } vec3 blendSoftLight(vec3 base, vec3 blend) { return mix( sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend), 2.0 * base * blend + base * base * (1.0 - 2.0 * blend), step(base, vec3(0.5)) ); } vec3 blendHardLight(vec3 base, vec3 blend) { return vec3( blend.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)), blend.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)), blend.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)) ); } vec3 blendAdd(vec3 base, vec3 blend) { return min(base+blend,vec3(1.0)); } float blendScreen(float base, float blend) { return 1.0-((1.0-base)*(1.0-blend)); } vec3 blendScreen(vec3 base, vec3 blend) { return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b)); } vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } vec3 rgb2hsv(in vec3 c) { vec4 K = vec4(0., -.33333333333333333333, .6666666666666666666, -1.); #ifdef RGB2HSV_MIX vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); #else vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy); vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx); #endif float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6. * d + e)), d / (q.x + e), q.x); } vec4 rgb2hsv(in vec4 c) { return vec4(rgb2hsv(c.rgb), c.a); } vec3 enhance(vec3 color, float brightness, float saturation, float contrast) { // enhance saturation float lum = luma(color.rgb); color = clamp(color + saturation * (color - lum), 0.05, 1.0); // enhance brightness color = clamp(color * (1.0 + brightness), 0.0, 1.0); // enhance contrast by blending towards the s-curve vec3 scurve = smoothstep(0.0, 1.0, color); color = mix(color, scurve, contrast); return color; } // Frame and SDF drawing functions // ------------------------------------------ float ndot(vec2 a, vec2 b ) { return a.x*b.x - a.y*b.y; } float gain(float x, float k) { float a = 0.5 * pow(2.0 * ((x < 0.5) ? x:1.0 - x), k); return (x < 0.5) ? a : 1.0 - a; } float clampedGain(float x, float k) { if (x <= 0.0) return x; if (x >= 1.0) return x; float a = 0.5 * pow(2.0 * mix(x, 1.0 - x, step(0.5, x)), k); return mix(a, 1.0 - a, smoothstep(0.49, 0.51, x)); } vec2 getLine(vec2 uv, vec2 p1, vec2 p2, float width, float t) { float a = (p2.y - p1.y) / (p2.x - p1.x); float b = p2.y - a * p2.x; float d = abs(uv.y - (uv.x * a + b)); d /= width * (2.0 - uv.y); d = 1.0 - d; d = clamp(d, 0.0, 1.0); vec2 c = t * (p2 - p1) + p1; float v = abs(c.x - uv.x) / (abs(p2.x - p1.x) * 0.5); v = 1.0 - v; v = clamp(v, 0.0, 1.0); return vec2(d, v); } float gennoise(vec2 uv) { return fract(sin(dot(uv,vec2(2.9898,8.233))) * 63758.123); } vec4 drawLines() { vec2 uv = (u_STMatrix * vec4(v_TexCoords.x, v_TexCoords.y, 0.0, 1.0)).xy; vec4 lineRes = vec4(0.0); float aspect = u_RenderSize.y / u_RenderSize.x / 2.0; vec2 leftTop = vec2(faceCenterX - faceWidth / 2.0, (1.0 - faceCenterY) + faceHeight * aspect); vec2 rightTop = vec2(faceCenterX + faceWidth / 2.0, (1.0 - faceCenterY) + faceHeight * aspect); vec2 leftBottom = vec2(faceCenterX - faceWidth / 2.0, (1.0 - faceCenterY) - faceHeight * aspect); vec2 rightBottom = vec2(faceCenterX + faceWidth / 2.0, (1.0 - faceCenterY) - faceHeight * aspect); // compute corners vec2 ps[4]; vec2 ps2[4]; float tL = 2.4; float timeNo = 1. + floor(u_Time / tL); float n1 = gennoise(vec2(timeNo)); float x = 0.5 * (leftTop.x + rightTop.x);// * (1.0 + 0.25 * (n1 - 0.5)); x = max(leftTop.x + 0.05, x); x = min(rightTop.x - 0.05, x); ps[0] = vec2(x, 1.0); // top float y; // compute y for left point float s = (ps[0].y - leftTop.y) / (ps[0].x - leftTop.x); y = ps[0].y - ps[0].x * s; y = clamp(y, leftTop.y - 0.05, leftBottom.y + 0.05); y += 0.05 * (n1 - 0.5); ps[1] = vec2(0.0, y); // left float y2; // compute y2 for right point s = -(ps[0].y - rightTop.y) / (ps[0].x - rightTop.x); y2 = ps[0].y - (1.0 - ps[0].x) * s; y2 = clamp(y2, leftTop.y - 0.05, leftBottom.y + 0.05); y2 += 0.05 * (n1 - 0.5); ps[3] = vec2(1.0, y2); // right float x2; float x21 = 1.0; float x22 = 0.0; bool leftSide = (leftTop.x - 0.0) < (1.0 - rightTop.x); if (leftSide) { float s = (ps[1].y - leftBottom.y) / (ps[1].x - leftBottom.x); x21 = ps[1].y / abs(s); } else { float s = (ps[3].y - rightBottom.y) / (ps[3].x - rightBottom.x); x22 = 1.0 - ps[3].y / abs(s); } x2 = 0.5 * (x21 + x22); x2 = clamp(x2, 0.35, 0.65); ps[2] = vec2(x2, 0.0); // bottom float gap = 0.14; ps2[0] = ps[0]; ps2[1] = ps[1]; ps2[2] = ps[2]; ps2[3] = ps[3]; ps2[0].y += gap * aspect * 2.0; ps2[1].x -= gap; ps2[3].x += gap; ps2[2].y -= gap * aspect * 2.0; int no = 10; for (int i = 0; i < no; i++) { float ratio = float(i) / float(no); float time = u_Time + ratio; time = mod(time, tL); int index = int(floor(time * 4.0 / tL)); float tc = time / tL; float qt = 0.25 * tL; float a = (time - float(index) * qt) / qt; float delta = 0.15 * (fract(timeNo * ratio) - 0.5); float smalldelta = delta * 0.5; //ps2[1].y += delta; ps2[0].x += smalldelta; ps2[2].x += smalldelta; //ps2[3].y += delta; //ps[1].y += delta; ps[0].x += delta; ps[2].x += delta; //ps[3].y += delta; vec3 timeColor = vec3(1.0 - 0.5 * tc, tc, 0.5 * tc + 0.5) + vec3(float(i) * 0.05, float(i) * 0.0333, 0.0); timeColor *= 1.333; { vec2 d = getLine(uv, ps[index], ps[int(mod(float(index + 1), 4.0))], 0.012, a); vec3 lineColor = timeColor; lineColor *= d.x * d.y * 0.5; lineRes.rgb += lineColor; } { vec2 d = getLine(uv, ps2[3 - index], ps2[int(mod(float(3 - index + 1), 4.0))], 0.012, 1. - a); vec3 lineColor = timeColor.gbr; lineColor *= d.x * d.y * 0.5; lineRes.rgb += lineColor; } //ps2[1].y -= delta; ps2[0].x -= smalldelta; ps2[2].x -= smalldelta; //ps2[3].y -= delta; //ps[1].y -= delta; ps[0].x -= delta; ps[2].x -= delta; //ps[3].y -= delta; } return lineRes; } vec3 textureLUT(in sampler2D tex_lut, in vec3 color) { const float LUT_SIZE = 32.0; const float LUT_SQUARE_SIZE = LUT_SIZE * LUT_SIZE; const vec2 LUT_STEPS = vec2(1.0/LUT_SQUARE_SIZE, 1.0/LUT_SIZE); vec3 scaledColor = color.rgb * (LUT_SIZE - 1.0); float bFrac = fract(scaledColor.z); // offset by 0.5 pixel and fit within range [0.5, width-0.5] // to prevent bilinear filtering with adjacent colors vec2 texc = (.5 + scaledColor.xy) * LUT_STEPS; // offset by the blue slice texc.x += (scaledColor.z - bFrac) * LUT_STEPS.y; // sample the 2 adjacent blue slices vec3 b0 = texture2D(tex_lut, texc).xyz; vec3 b1 = texture2D(tex_lut, vec2(texc.x + LUT_STEPS.y, texc.y)).xyz; // blend between the 2 adjacent blue slices color.xyz = mix(b0, b1, bFrac); return color; } void main() { vec2 uv = (u_STMatrix * vec4(v_TexCoords.x, v_TexCoords.y, 0.0, 1.0)).xy; vec2 segUV = (u_STMatrix1 * vec4(v_TexCoords.x, v_TexCoords.y, 0.0, 1.0)).xy; // Calculate flickering float t = uFlickerProgress * TWO_PI; float flicker = max(cos(t) * cos(t * 4.0) * 0.75 + 0.25, 0.0); // Calculate how frame scale will change with face distance // a higher pow will make the scale change faster with distance. 2.0 is the pysically correct one float rad = uInnerScale * pow(uFrameScale, uInnerScaleChange); float rad2 = uOuterScale * pow(uFrameScale, uOuterScaleChange); // Calculate frame shape vec2 res = u_RenderSize.xy / u_RenderSize.y; vec2 frameUv = uv * res; vec2 center = vec2(uFrameX, uFrameY) * res; float brightness = uShapeBrightness * flicker; vec3 frameCenter; vec3 frameSharp; vec3 frameGlow; // Rotate the UV's for a 45 degree gradient vec2 angledUv = rotateUV((uv - 0.5) * res, -0.867, vec2(0.0)) + 0.5; float displacement = sin(u_Time * uRotationSpeed * 5.0) * uRotationRange; // The gradient texture is equally spaced so we need to "stretch" the middle const float scale = 1.35; const float offset = -0.01; angledUv.x = (angledUv.x - 0.5) * scale + 0.5 + offset; angledUv.x = clamp(angledUv.x, 0.01, 0.99); angledUv.x = gain(angledUv.x , uGradientStretch); angledUv.x += displacement; vec3 clrMixed = texture2D(u_Gradient, angledUv).rgb; //frame //Triangle frameCenter = drawLines().rgb; frameSharp = drawLines().rgb * brightness; frameGlow = drawLines().rgb * 0.75 * mix(brightness, 1.0, 0.5); vec3 frame = min(frameSharp + frameGlow, 1.0) * clrMixed + min(frameCenter, 1.0) * mix(clrMixed, vec3(1.0), 0.75); frame = clamp(frame, 0., 1.); // add color wash to the whole frame float screenVignette = (1.0 - length(uv - vec2(0.5))) * 0.2 + 0.8; float faceVignette = (length(uv - center) * 0.25) * 0.65 + 0.1; vec3 colorWashHsv = vec3(rgb2hsv(clrMixed).x, faceVignette * screenVignette, screenVignette); vec3 colorWash = hsv2rgb(colorWashHsv); // apply segmentation const float e = 0.03; float segGradient = smoothstep(center.y - e, center.y + e, uv.y); float segMask = texture2D(u_Segmentation, segUV).r; vec3 segmentedFrame = frame * (1.0 - segMask * segGradient); // Face relighting vec4 camImage = texture2D(u_Texture, uv); camImage.rgb = enhance(camImage.rgb, uBrightness, uSaturation, uContrast); vec4 rimMask = texture2D(u_InputMask, uv); vec3 faceColor = clrMixed * rimMask.rgb * 1.0 * flicker; float lum = pow(luma(camImage.rgb), 0.7);// * pow(polarUv.x, 0.4); // Blend everything vec3 camBlend = blendScreen(camImage.rgb, faceColor * lum); // vec3 washBlend = mix(camBlend, camBlend * colorWash.rgb, polarUv.x); vec3 washBlend = mix(camBlend, camBlend, 1.0); vec3 finalBlend = blendScreen(washBlend, segmentedFrame); gl_FragColor = vec4(finalBlend.rgb, 1.0); gl_FragColor.rgb = textureLUT(u_Lut, gl_FragColor.rgb); }