【问题标题】:SpriteKit Issue With SKShader Animation In SwiftSwift 中 SKShader 动画的 SpriteKit 问题
【发布时间】:2018-11-25 20:42:51
【问题描述】:

对于我的生活,我无法理解为什么这个着色器动画没有运行。任何帮助表示赞赏。请查看下面的代码...着色器正确显示,但没有动画。谢谢。

注意:Xcode 9.4.1、Swift 4.1、iOS 11.4

import SpriteKit

class GameScene: SKScene {
    static let marquee: SKShader = {
        let shader = SKShader(
            source: "void main() {" +
                "float np = mod(a_path_phase + v_path_distance / u_path_length, 1.0);" +
                "vec3 hsb = vec3(np, 1.0, 1.0);" +
                "vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
                "vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
                "vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
                "vec4 df = SKDefaultShading();" +
                "gl_FragColor = vec4(rgb, df.z);" +
            "}"
        )
        shader.attributes = [SKAttribute(name: "a_path_phase", type: .float)]
        return shader
    } ()

    private let beat: TimeInterval = 0.05
    private var phase: Float = 0

    override func didMove(to view: SKView) {
        let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
        node.fillColor = .clear
        node.strokeColor = .white
        node.strokeShader = GameScene.marquee
        node.lineWidth = 8
        node.glowWidth = 4
        node.run(
            SKAction.repeatForever(
                SKAction.sequence(
                    [
                        SKAction.wait(forDuration: beat),
                        SKAction.run() { [weak self] in
                            if let weak = self {
                                weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
                                node.setValue(SKAttributeValue(float: weak.phase), forAttribute: "a_path_phase")
                            }
                        }
                    ]
                )
            )
        )
        addChild(node)
    }
}

【问题讨论】:

  • 嗨!你有没有碰巧想出这个?到目前为止,对我来说,属性值似乎没有到达着色器,着色器似乎总是得到 value = 0.0;就好像 node.setValue(,forAttribute:) 命令被忽略了。可能是iOS错误,还是我遗漏了什么?我希望这不是一个错误。我希望我错过了什么。

标签: swift sprite-kit shader


【解决方案1】:

我也遇到了.setValue(forAttribute:) 的问题。它似乎不起作用。经过调查,我的结论是……

我相信SKShapeNode.setValue(forAttribute:) 不起作用。

SKSpriteNode.setValue(forAttribute:) 在相同条件下确实可以正常工作这一事实让我得出结论,这几乎可以肯定是SKShapeNode 中的 iOS 中的一些错误。

事实上,我可能设置的任何SKAttributes SKShapeNode.fillShaderSKShapeNode.strokeShader 保留他们的 无论我做什么,值都设置为0.0

但是,SKSpriteNode.shader 却没有发生同样的情况。

所以我必须想办法绕过这个错误。 某种黑客行为。 幸运的是,就我而言,我确实找到了办法。

而且幸运的是,它似乎也足以解决您的问题。


在这种情况下,您可以做的是将 phase 的值作为strokeColor 的组件之一传递,例如,红色组件。

为此,您必须将 阶段 标准化为介于 0.01.0 之间的值。 您可以使用 shader.uniform 传递 phase multiplier 以帮助对其进行非规范化。

然后从SKDefaultShading().x(红色分量)读取着色器内的相位值。

确保设置 alpha 组件 strokeColor1.0,对于 SKDefaultShading() 将有它的颜色 组件值已经预乘了它的 alpha。

现在,因为您的 strokeColor 已被用于传递 phase,您可以将描边线的实际颜色作为vec4 shader.uniform.


  • 就我而言,我还使用了u_time 来帮助执行我的 动画。像fract(u_time) 这样的东西非常有用。

修正后的代码如下所示:

> 注意:有一个更简单的解决方案。看这篇文章的结尾。

import SpriteKit

class GameScene: SKScene {
    private typealias Me = GameScene
    static let phaseMultiplier: Float = 1000

    static let marquee: SKShader = {
        let shader = SKShader(
            source: "void main() {" +
                "float red = SKDefaultShading().x;" +
                "float phase = red * u_phase_multiplier;" +
                "float np = mod(phase + v_path_distance / u_path_length, 1.0);" +
                "vec3 hsb = vec3(np, 1.0, 1.0);" +
                "vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
                "vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
                "vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
                "vec4 df = u_stroke_color;" +
                "gl_FragColor = vec4(rgb, df.z);" +
            "}"
        )
        let white = vector_float4.init(1, 1, 1, 1)
        shader.uniforms = [
            SKUniform(name: "u_phase_multiplier", float: Me.phaseMultiplier), 
            SKUniform(name: "u_stroke_color", vectorFloat4: white)]
        return shader
    } ()

    private let beat: TimeInterval = 0.05
    private var phase: Float = 0

    override func didMove(to view: SKView) {
        let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
        node.fillColor = .clear
        node.strokeColor = .white
        node.strokeShader = Me.marquee
        node.lineWidth = 8
        node.glowWidth = 4
        node.run(
            SKAction.repeatForever(
                SKAction.sequence(
                    [
                        SKAction.wait(forDuration: beat),
                        SKAction.run() { [weak self] in
                            if let weak = self {
                                weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
                                let phase = weak.phase / Me.phaseMultiplier
                                node.strokeColor = SKColor.init(red: phase, green: 0, blue: 0, alpha: 1)
                            }
                        }
                    ]
                )
            )
        )
        addChild(node)
    }
}

注意:我没有编译上面发布的代码。我应该只是一个草图。


发帖后的第二天,我发现attribute参数不应该在fragment shaders中使用,只能在vertex shaders中使用.

看看 StackOverflow 问题: In WebGL what are the differences between an attribute, a uniform, and a varying variable?

但是,SKAttributeSKAttributeValue 的 SDK 文档似乎表明“SKAttributes”和实际着色器属性参数之间可能没有直接关系。它们似乎只是命名相同。


更简单的解决方案:

然而,最后,永远不需要像我在这里展示的那样使用任何 hack。

所有需要做的就是通过SKShader.uniformNamed("u_phase")?.floatValue = phase设置一个uniform的值,而不是像jmdecombe那样使用.setValue(forAttribute:),正确,稍后指出出去。

我将把我的答案留在这里以供参考,因为它为问题提供了一种非常规的解决方案。

【讨论】:

  • 嗨,已经有一段时间了,但我看了一下代码,确实解决方案是使用制服而不是属性,然后它就起作用了。
  • 即行shader.attributes = etc.必须变成:shader.uniforms = [SKUniform(name: "a_path_phase", float: 0.0)],那么行node.setValue etc.有变成:node.strokeShader?.uniformNamed("a_path_phase")?.floatValue = weak.phase
  • 你在这里告诉我的是你决定写一个统一而不是一个属性,我没有意识到这是可能的。不过,我们实现需求的两种“替代”方式似乎都证实了一件事,那就是 SKShapeNode.setValue(,forAttribute:) 中存在一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多