前文回顾:

【Flutter&GLSL】用Fragment Shader来完成高功能的动画作用——翻页动画(一)

在之前的文章中,现已完成了最基本的翻页动画,不过间隔适配与可用,还差两个小目标:

  • 翻页暗影的完成
  • 翻页限制和对应手势信息的处理转换

这次就接着对这两部进行处理,首要就来看下暗影部分的完成。

首要仍是奉上这次完成后的作用:

【Flutter&GLSL】用Fragment Shader来完成高功能的动画作用——翻页动画的暗影完成

现在回到正题,看下暗影作用应该怎样完成出来。

如何通过GLSL来完成一个暗影作用

暗影作用的完成,说白了便是一个黑色突变为通明的进程,在glsl中,只要提供一个突变的数值,那么暗影就能够依据这个突变数值来完成出来,因而第一步就需求找到这个突变数值。

这儿首要仍是回顾下原理:

【Flutter&GLSL】用Fragment Shader来完成高功能的动画作用——翻页动画的暗影完成

如图中所示,各个区域的制作是依据目标纹路方位在鼠标方向上映射,减去鼠标和核算原点的间隔所得的dist(当然现稍有区别,会再减去一部分来让翻页部分跟随手指)来判别区分各个区域;

  • 假如dist大于弯曲作用的半径,那么视为现已翻过去的部分;
  • 假如dist大于0小于半径,那么视为正在弯曲的部分;
  • 假如dist小于0,则视为还未弯曲正常展现的部分;

按照这个区分逻辑,能够看到dist是一个从大到小的突变数值,在包含了鼠标方向之外,还结合上了纹路方位这个因素,是一个相对比较合适的判别依据。


咱们在获得了当时突变数值的一同,还需求得知暗影延申的方向,以及暗影的规模巨细

在已翻页部分,这个问题比较好解决:

因为dist是其在鼠标方向上的映射间隔,天然包含了鼠标方向,这个便是暗影的方向,因而咱们只需求依据dist的巨细来规则其突变作用即可,比如说像这样,依据dist的值修改一个黑色色彩的通明度:

vec4(0.0, 0.0, 0.0, (1.0 - pow(clamp((dist - radius)*pi, 0.0, 1.0), 0.2)));

比较麻烦的是正在翻页的部分和现已翻页的部分:

因为翻页部分的暗影应该围绕翻起页脚,且方向同册页方向,这样dist这种包含鼠标方向的数值就不能使用了。咱们需求寻觅一个新的数值。

在翻起页的核算进程中,会核算当时像素方位对应在纹路上的方位,其间就有一个判别在蜷曲轴上是否有多个映射点的进程。因而能够这么想象一下,暗影便是一个册页纹路未蜷曲前外面围绕的一圈,假如这包裹的一圈能随着册页一同蜷曲起来,那不就完成了暗影作用么?

【Flutter&GLSL】用Fragment Shader来完成高功能的动画作用——翻页动画的暗影完成

依据这个设想,能够给册页纹路在添加一些映射规模,假如存在多个映射点,那说明正好是需求制作暗影的部分:

if (p2.x <= aspect+shadowWidth && p2.y <= 1.0+shadowWidth&& p2.x > 0.0-shadowWidth && p2.y > 0.0-shadowWidth){
   /// todo;判别暗影色彩
}

对于暗影色彩作用的完成,那就依据当时映射方位间隔册页纹路方位的巨细判别就行,相似这样:

if (targetPoint.y>=1.0){
   return max(pow(clamp((targetPoint.y-1.0)/shadowWidth, 0.0, 0.9), 0.2),pow(clamp((targetPoint.x-aspect)/shadowWidth, 0.0, 0.9), 0.2));
} else { 
   return max(pow(clamp((0.0-targetPoint.y)/shadowWidth, 0.0, 0.9), 0.2),pow(clamp((targetPoint.x-aspect)/shadowWidth, 0.0, 0.9), 0.2)); 
}

因为实践翻页角度并不像上图所示,彻底横向的那种,因而需求判别两个方向上的色彩,这儿直接取两个方向上较大的作为暗影的突变数值。

shader完好代码

#include <flutter/runtime_effect.glsl>
uniform vec2 resolution;
uniform vec4 iMouse;
uniform sampler2D image;
#define pi 3.14159265359
#define radius 0.05
#define shadowWidth 0.02
#define TRANSPARENT vec4(0.0, 0.0, 0.0, 0.0)
out vec4 fragColor;
float calShadow(vec2 targetPoint, float aspect){
    if (targetPoint.y>=1.0){
        return max(pow(clamp((targetPoint.y-1.0)/shadowWidth, 0.0, 0.9), 0.2), pow(clamp((targetPoint.x-aspect)/shadowWidth, 0.0, 0.9), 0.2));
    } else {
        return max(pow(clamp((0.0-targetPoint.y)/shadowWidth, 0.0, 0.9), 0.2), pow(clamp((targetPoint.x-aspect)/shadowWidth, 0.0, 0.9), 0.2));
    }
}
void main() {
    vec2 fragCoord = FlutterFragCoord().xy;
    float aspect = resolution.x / resolution.y;
    vec2 uv = fragCoord * vec2(aspect, 1.0) / resolution.xy;
    // 归一化鼠标坐标
    vec2 mouse = iMouse.xy  * vec2(aspect, 1.0) / resolution.xy;
    vec2 cornerFrom = (iMouse.w<resolution.y/2)?vec2(resolution.x, 0.0):vec2(resolution.x, resolution.y);
    // 鼠标方向的向量
    vec2 mouseDir = normalize(abs(cornerFrom) - iMouse.xy);
    // 翻页原点的核算,能够视为转换为横轴下的x轴起点方位
    vec2 origin = clamp(mouse - mouseDir * mouse.x / mouseDir.x, 0.0, 1.0);
    // 鼠标间隔
    float mouseDist = distance(mouse, origin);
    //    float mouseDist = clamp(length(mouse - origin)
    //    + (aspect - (abs(cornerFrom.x) / resolution.x) * aspect) / mouseDir.x, 0.0, aspect / mouseDir.x);
    // 假如鼠标方向向左,那么鼠标拖动间隔便是鼠标到原点的间隔
    if (mouseDir.x < 0.0) {
        mouseDist = distance(mouse, origin);
    }
    float proj = dot(uv - origin, mouseDir);
    float dist = proj - mouseDist;
    vec2 curlAxisLinePoint = uv - dist * mouseDir;
    if (distance(mouse, cornerFrom* vec2(aspect, 1.0) / resolution.xy)>=pi*radius) {
        float params = (distance(mouse, cornerFrom* vec2(aspect, 1.0) / resolution.xy)-pi*radius)/2;
        curlAxisLinePoint = uv - dist * mouseDir +params*mouseDir;
        dist -=params;
    }
    if (dist > radius) {
        fragColor = vec4(0.0, 0.0, 0.0, (1.0 - pow(clamp((dist - radius)*pi, 0.0, 1.0), 0.2)));
    } else if (dist >= 0.0) {
        // map to cylinder point
        float theta = asin(dist / radius);
        vec2 p2 = curlAxisLinePoint + mouseDir * (pi - theta) * radius;
        vec2 p1 = curlAxisLinePoint + mouseDir * theta * radius;
        if (p2.x <= aspect && p2.y <= 1.0 && p2.x > 0.0 && p2.y > 0.0){
            uv = p2;
            fragColor = texture(image, uv * vec2(1.0 / aspect, 1.0));
            fragColor.rgb *= pow(clamp((radius - dist) / radius, 0.0, 1.0), 0.2);
        } else {
            uv = p1;
            fragColor = texture(image, uv * vec2(1.0 / aspect, 1.0));
            if (p2.x <= aspect+shadowWidth && p2.y <= 1.0+shadowWidth&& p2.x > 0.0-shadowWidth && p2.y > 0.0-shadowWidth){
                float shadow = calShadow(p2, aspect);
                fragColor = vec4(fragColor.r*shadow, fragColor.g*shadow, fragColor.b*shadow, fragColor.a);
            }
        }
    } else {
        vec2 p = curlAxisLinePoint + mouseDir * (abs(dist) + pi * radius);
        if (p.x <= aspect && p.y <= 1.0 && p.x > 0.0 && p.y > 0.0){
            uv = p;
            fragColor = texture(image, uv * vec2(1.0 / aspect, 1.0));
        } else {
            fragColor = texture(image, uv * vec2(1.0 / aspect, 1.0));
            if (p.x <= aspect+shadowWidth && p.y <= 1.0+shadowWidth&& p.x > 0.0-shadowWidth && p.y > 0.0-shadowWidth){
                float shadow = calShadow(p, aspect);
                fragColor = vec4(fragColor.r*shadow, fragColor.g*shadow, fragColor.b*shadow, fragColor.a);
            }
        }
    }
}

小结

现在现已完成了暗影部分的作用,再剩余的部分便是添加翻页规模的限制,以应对过度翻页。依据现在的成果来看,功能方面仍是非常满足的,基本能保证FPS>=50的比率高于95%,没有BigJank的状况,基本能够视为不会出现卡顿的状况了。

在完成了翻页规模的限制之后,就需求将这个翻页动画应用到小说阅读器上,在应用小说阅读器的进程中,或许能够再次审视一下分页功能和负责手势处理的ListView?