漩涡滤镜
顾名思义,这个滤镜效果就像一个🌀,在一个半径范围内,从中心到边缘,旋转角度逐渐减小。
那么在代码实现中,就是要在指定的半径范围内,把当前采样点旋转一定程度,并且用当前采样点的纹素覆盖掉原本这个位置的纹素。当前采样点的旋转角度也要随着它距离中心点的距离,越近越大。

这个滤镜效果的主要代码都在片元着色器中:
precision highp float;
//纹理坐标
varying vec2 textureCoordVarying;
//纹理采样器
uniform sampler2D textureSampler;
//旋转角度
const float rotateDegrees = 150.0;
//旋转半径(相对于纹理坐标)
const float rotateRadius = 0.5;
void main() {
//设置滤镜范围
ivec2 filterSize = ivec2(512, 512);
//获取滤镜范围内的直径
float diameter = float(filterSize.y);
//获取滤镜范围内需要旋转的半径
float radius = rotateRadius * diameter;
//纹理坐标
vec2 st = textureCoordVarying;
//当前纹理坐标在滤镜范围内的位置
vec2 currentPosition = st * diameter;
//获取currentPosition相对于圆心(纹理坐标(0.5, 0.5)的位置)的那一段向量
//把xy向左向下偏移一个半径的长度,后面会再移回原位,这样是为了方便计算xy到圆心的距离
vec2 distanceXY = currentPosition - vec2(diameter / 2.0, diameter / 2.0);
//获取currentPosition到圆心的距离
float r = length(distanceXY);
//递减因子
float factor = 1.0 - (r / radius) * (r / radius);
//最终旋转角度 = 当前的角度 + 需要旋转的角度 * 递减因子
float beta = atan(distanceXY.y, distanceXY.x) + radians(rotateDegrees) * factor;
//如果currentPosition距离圆心的距离小于等于半径
if (r <= radius) {
//计算旋转后的位置
distanceXY = r * vec2(cos(beta), sin(beta));
//平移回原位
currentPosition = vec2(diameter / 2.0, diameter / 2.0) + distanceXY;
}
//旋转后的纹理坐标
st = currentPosition / diameter;
//旋转后的纹素
vec3 rotatedRGB = texture2D(textureSampler, st).rgb;
gl_FragColor = vec4(rotatedRGB, 1.0);
}
详细解析
- 首先看几个重要的变量
textureCoordVarying:从顶点着色器传过来的纹理坐标textureSampler:uniform通道传入的纹理rotateDegrees:可以旋转的最大角度rotateRadius:旋转的半径
- main函数
- 定义变量
- 设置一个滤镜范围
filterSize,把纹理坐标(0~1之间)转为范围值,方便计算 - 设置直径
diameter并计算出半径radius - 由于需要计算旋转后的位置,另外定义一个变量
st来存储纹理坐标 - 当前纹理坐标
st✖️ 滤镜范围的直径diameter,就是这个坐标点在这个滤镜范围内的位置currentPosition
- 设置一个滤镜范围
- 计算当前位置
currentPosition距离圆心的半径r-
先把当前位置
currentPosition向左向下平移一个半径radius的长度。为什么要这样平移呢? -
首先纹理坐标的原点是在左下角,而我们要计算当前点距离圆心的位置,而圆心和原点之间的距离就是左下角方向一个半径的偏移量。
-

-
比如图中有两个点,点O`平移到了点O',点P平移到了定p',O到圆心的距离和角度都和O'到原点的距离和角度一样。
-
这样偏移到原点位置,就可以很方便的用坐标的x,y来计算角度了。后面会再移回原处的。
-
用
length函数就可以求得平移后的向量distanceXY的长度了。也就是当前位置距离圆心的半径长度(图中红色和绿色的虚线)。
-
- 计算旋转角度
- 抛物线递减因子:距离圆心越近,旋转角度越大;
- float factor = 1.0 - (r / radius) * (r / radius);
- 当前位置的角度:
distanceXY向量的atan值;(下图中角θ) - tan θ = 对边 / 邻边
- tan θ =
y'/x' - tan θ =
distanceXY.y/distanceXY.x - atan函数求反正切值

- 再➕(需要旋转的角度✖️递减因子)
- 等于最终角度
beta 
- 计算旋转后的位置
- 如果当前位置
currentPosition距离圆心的半径r小于等于旋涡半径radius,就计算旋转后的位置; - 根据旋转角度的余弦值
cos(beta)和正弦值sin(beta)乘以半r径计算旋转后位置的两个边长,也就是x,y的值; - cos beta = 邻边 / 斜边
- cos beta =
x''/r - sin beta = 对边 / 斜边
- sin beta =
y''/r - (cos(beta), sin(beta)) * r = (
x'',y'') 
- 再反向平移回原处,得到旋转后的位置;
- 如果当前位置
- 范围值转换成旋转后的纹理坐标
st - 获取旋转后的纹素
rotatedRGB的颜色值 - 使用旋转后的颜色
- 定义变量