Собственно вот, написал Swirl-shader. Результат можно посмотреть на флешке.
Эффект перемещается за курсором.
1) Во фрагментный шейдер посылаются 2 константы, первая содержит в себе позицию эффекта, радиус и угол закручивания. Вторая константа содержит в себе ширину и высоту экрана и необходимые константы.

Код AS3:
vertexCode += "mov op, va0\n";
vertexCode += "mov v0, va1\n"; // uv
fragmentCode = "mov ft0, v0\n"; // uv
// ---- SWIRL ----
// fc0 = [x = 300, y = 300, radius = 400, angle = 0.8]
// fc1 = [stageWidth, stageHeight, 8.0, 0]
fragmentCode += "mul ft0.x, ft0.x, fc1.x\n"; // tc.x = tc.x * stageWidth
fragmentCode += "mul ft0.y, ft0.y, fc1.y\n"; // tc.y = tc.y * stageHeight
fragmentCode += "mov ft0.z, fc1.w\n"; // tc.z = 0
fragmentCode += "sub ft0.x, ft0.x, fc0.x\n"; // dx = tc.x - center.x
fragmentCode += "sub ft0.y, ft0.y, fc0.y\n"; // dy = tc.y - center.y
fragmentCode += "mul ft1.x, ft0.x, ft0.x\n"; // dx * dx
fragmentCode += "mul ft1.y, ft0.y, ft0.y\n"; // dy * dy
fragmentCode += "add ft1.x, ft1.x, ft1.y\n"; // dx * dx + dy * dy
fragmentCode += "sqt ft1.x, ft1.x\n"; // dist = sqrt(dx * dx + dy * dy)
fragmentCode += "slt ft1.w, ft1.x, fc0.z\n"; // if (dist < radius)
fragmentCode += "sub ft1.y, fc0.z, ft1.x\n"; // (radius - dist)
fragmentCode += "div ft1.y, ft1.y, fc0.z\n"; // percent = (raius - dist) / raius
fragmentCode += "mul ft1.y, ft1.y, ft1.y\n"; // percent * percent
fragmentCode += "mul ft1.y, ft1.y, fc0.w\n"; // percent * percent * angle
fragmentCode += "mul ft1.y, ft1.y, fc1.z\n"; // theta = percent * percent * angle * 8.0
fragmentCode += "cos ft1.z, ft1.y\n"; // c = cos(theta)
fragmentCode += "sin ft1.y, ft1.y\n"; // s = sin(theta)
fragmentCode += "mov ft2.x, ft1.y\n"; // vec(s, ?, ?)
fragmentCode += "mov ft2.y, ft1.z\n"; // vec(s, c, ?)
fragmentCode += "mov ft2.z, ft0.z\n"; // vec(s, c, 0)
fragmentCode += "dp3 ft1.x, ft0.xyz, ft2.xyz\n"; // dot(tc, vec(s, c, 0)) : y
fragmentCode += "neg ft1.y, ft1.y\n"; // s = -s
fragmentCode += "mov ft2.x, ft1.z\n"; // vec(c, ?, 0)
fragmentCode += "mov ft2.y, ft1.y\n"; // vec(c, -s, 0)
fragmentCode += "dp3 ft1.y, ft0.xyz, ft2.xyz\n"; // dot(tc, vec(c, -s, 0)) : x
fragmentCode += "mul ft1.y, ft1.y, ft1.w\n"; // if (dist < radius) ? 1 : 0
fragmentCode += "mul ft1.x, ft1.x, ft1.w\n"; // if (dist < radius) ? 1 : 0
fragmentCode += "seq ft1.w, ft1.w, fc1.w\n"; // if (uv == 0) ? uv = 1 : 0
fragmentCode += "mul ft0.xy, ft0.xy, ft1.w\n"; // tc.xy = if (uv == 1) ? tc.xy - center.xy : 0
fragmentCode += "add ft0.xy, ft0.xy, ft1.yx\n"; // tc.xy = if (uv == 0) ? vec(dot(tc, vec(c, -s, 0)), dot(tc, vec(s, c, 0))) : 0
fragmentCode += "add ft0.x, ft0.x, fc0.x\n"; // tc.x = tc.x + center.x
fragmentCode += "add ft0.y, ft0.y, fc0.y\n"; // tc.y = tc.y + center.y
fragmentCode += "div ft0.x, ft0.x, fc1.x\n"; // tc.x = tc.x / stageWidth
fragmentCode += "div ft0.y, ft0.y, fc1.y\n"; // tc.y = tc.y / stageHeight
// ---- END SWIRL ----
fragmentCode += "tex ft0, ft0, fs0 <2d,nearest,clamp>\n";
fragmentCode += "mov oc, ft0\n";
Послесловие: По какой-то причине у меня не работает AGAL2, из-за чего условия пришлось сделать кустарным методом.