前文回顾:
【Flutter&GLSL】用Fragment Shader来完成高功能的动画作用——翻页动画(一)
在之前的文章中,现已完成了最基本的翻页动画,不过间隔适配与可用,还差两个小目标:
- 翻页暗影的完成
- 翻页限制和对应手势信息的处理转换
这次就接着对这两部进行处理,首要就来看下暗影部分的完成。
首要仍是奉上这次完成后的作用:
现在回到正题,看下暗影作用应该怎样完成出来。
如何通过GLSL来完成一个暗影作用
暗影作用的完成,说白了便是一个黑色突变为通明的进程,在glsl中,只要提供一个突变的数值,那么暗影就能够依据这个突变数值来完成出来,因而第一步就需求找到这个突变数值。
这儿首要仍是回顾下原理:
如图中所示,各个区域的制作是依据目标纹路方位在鼠标方向上映射,减去鼠标和核算原点的间隔所得的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这种包含鼠标方向的数值就不能使用了。咱们需求寻觅一个新的数值。
在翻起页的核算进程中,会核算当时像素方位对应在纹路上的方位,其间就有一个判别在蜷曲轴上是否有多个映射点的进程。因而能够这么想象一下,暗影便是一个册页纹路未蜷曲前外面围绕的一圈,假如这包裹的一圈能随着册页一同蜷曲起来,那不就完成了暗影作用么?
依据这个设想,能够给册页纹路在添加一些映射规模,假如存在多个映射点,那说明正好是需求制作暗影的部分:
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?