在 Starling 中,如何变换 Filters 以匹配目标 Sprite 的旋转和位置?

In Starling, how do you transform Filters to match the target Sprite's rotation & position?

假设您的 Starling 显示列表如下:

Stage
  |___MainApp
         |______Canvas (filter's target)

然后,您决定 MainApp 应该旋转 90 度并偏移一点:

 mainApp.rotation = Math.PI * 0.5;
 mainApp.x = stage.stageWidth;

但是突然之间,滤镜继续以原来的角度(好像 MainApp 仍处于 0 度)将自身应用于目标 (canvas)。

(请注意 GIF 中模糊的强水平值如何继续仅水平应用,尽管父对象已旋转 90 度)。

在目标对象进行父转换之前,需要更改什么才能将过滤器应用于目标对象?这样(我假设)过滤器的结果将被父对象转换。

想知道如何做到这一点吗?

https://github.com/bigp/StarlingShaderIssue

(PS:我实际使用的滤镜是定制的,但是这个 BlurFilter 示例显示了我在使用自定义滤镜时遇到的相同问题。如果需要进行任何修补着色器代码,至少它不一定必须专门在内置的 BlurFilter 上完成)。

我在几个小时内通过多次反复试验自己解决了这个问题。

因为我只需要着色器在 0 度或 90 度时 运行(实际上并不像问题中显示的 gif 演示那样补间),所以我创建了一个具有两组专门的 AGAL 指令的着色器。

无需详细说明,旋转版本基本上需要一些额外的指令来翻转顶点和片段着色器中的 x 和 y 字段(通过使用 mov 移动它们或直接计算 muldiv 结果进入 x 或 y 字段)。

例如,比较 0 度顶点着色器...

_vertexShader = [
    "m44 op, va0, vc0",     // 4x4 matrix transform to output space
    "mov posOriginal, va1",         // pass texture positions to fragment program
    "mul posScaled, va1, viewportScale",        // pass displacement positions (scaled)
].join("\n");

...使用 90 度顶点着色器:

_vertexShader = [
    "m44 op, va0, vc0",     // 4x4 matrix transform to output space
    "mov posOriginal, va1",         // pass texture positions to fragment program

    //Calculate the rotated vertex "displacement" UVs
    "mov temp1, va1",
    "mov temp2, va1",
    "mul temp2.y, temp1.x, viewportScale.y", //Flip X to Y, and scale with viewport Y
    "mul temp2.x, temp1.y, viewportScale.x", //Flip Y to X, and scale with viewport X
    "sub temp2.y, 1.0, temp2.y", //Invert the UV for the Y axis.
    "mov posScaled, temp2",
].join("\n");

You can ignore the special aliases in the AGAL example, they're essentially posOriginal = v0, posScaled = v1 variants and viewportScale = vc4constants, then I do a string-replace to change them back to their respective registers & fields ).

Just a human-readable trick I use to avoid going insane. \☻/

我最费劲的部分是计算正确的比例以调整 UV 的比例(适当检测舞台/视口调整大小和 render-texture 大小偏移)。

最终,这就是我在 AS3 代码中得出的结果:

var pt:Texture = _passTexture,
    dt:RenderTexture = _displacement.texture,
    notReady:Boolean = pt == null,
    star:Starling = Starling.current;

var finalScaleX:Number, viewRatioX:Number = star.viewPort.width / star.stage.stageWidth;
var finalScaleY:Number, viewRatioY:Number = star.viewPort.height / star.stage.stageHeight;

if (notReady) {
    finalScaleX = finalScaleY = 1.0;
} else if (isRotated) {
    //NOTE: Notice how the native width is divided with height, instead of same side. Weird, but it works!
    finalScaleY = pt.nativeWidth / dt.nativeHeight / _imageRatio / paramScaleX / viewRatioX; //Eureka!
    finalScaleX = pt.nativeHeight / dt.nativeWidth / _imageRatio / paramScaleY / viewRatioY; //Eureka x2!
} else {
    finalScaleX = pt.nativeWidth / dt.nativeWidth / _imageRatio / viewRatioX / paramScaleX;
    finalScaleY = pt.nativeHeight / dt.nativeHeight / _imageRatio / viewRatioY / paramScaleY;
}

希望这些提取的代码片段能对其他有类似着色器问题的人有所帮助。

祝你好运!