在 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
移动它们或直接计算 mul
或 div
结果进入 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 = vc4
constants, 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;
}
希望这些提取的代码片段能对其他有类似着色器问题的人有所帮助。
祝你好运!
假设您的 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
移动它们或直接计算 mul
或 div
结果进入 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 andviewportScale = vc4
constants, 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;
}
希望这些提取的代码片段能对其他有类似着色器问题的人有所帮助。
祝你好运!