Swift中SKShader动画的SpriteKit问题

Swift中SKShader动画的SpriteKit问题,swift,sprite-kit,shader,Swift,Sprite Kit,Shader,就我而言,我无法理解为什么这个着色器动画没有运行。感谢您的帮助。请看下面的代码。。。着色器将正确显示,但不会设置动画。多谢各位 注: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() {" +

就我而言,我无法理解为什么这个着色器动画没有运行。感谢您的帮助。请看下面的代码。。。着色器将正确显示,但不会设置动画。多谢各位

注: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)
    }
}

我在
.setValue(for属性:)
方面也遇到了问题。它似乎不起作用。经调查,我的结论是

我相信
SKShapeNode.setValue(对于属性:)
不起作用

事实上,
SKSpriteNode.setValue(for属性:)
在相同的条件下工作正常,这让我得出结论,这几乎肯定是iOS中
SKShapeNode
中的某个bug

事实上,我可能会设置任何
SKAttribute
s
SKShapeNode.fillShader
SKShapeNode.strokeShader
保留其 无论我做了什么,值都设置为
0.0

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

所以我不得不想办法避开这个错误。 某种黑客。 幸运的是,就我而言,我确实找到了一种方法

幸运的是,这似乎也足以解决你的问题


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

为此,您必须将阶段标准化为
0.0
1.0
之间的值。 您可以使用shader.uniform传递一个阶段乘法器,以帮助对其进行反规范化

然后从
SKDefaultShading().x
,(红色组件)读取着色器内部的阶段值

确保设置
strokeColor
1.0
,对于
SKDefaultShading()
将有其颜色 组件值已被其alpha预乘

现在,由于您的strokeColor已用于通过阶段,因此您可以将笔划线的实际颜色作为vec4 shader.uniform进行传递


  • 就我而言,我还利用
    u_time
    来帮助执行我的任务 动画像
    fract(u_时间)
    这样的东西非常有用

下面是更正后的代码的外观:

>注意:有一个更简单的解决方案。看看这篇文章的结尾。 注意:我没有编译上面发布的代码。我应该只是作为一个草图


在这篇文章的第二天,我发现属性参数不应该在片段着色器中使用,只应该在顶点着色器中使用

看看StackOverflow问题:

然而,
SKAttribute
SKAttributeValue
的SDK文档似乎表明,“SKAttributes”和实际着色器属性参数之间可能没有直接关系。它们的名字似乎是一样的


更简单的解决方案: 然而,最终,没有任何必要像我在这里展示的那样使用任何黑客

需要做的只是通过
SKShader.uniformNamed(“u_阶段”)?.floatValue=phase来设置统一的值,而不是使用
.setValue(for属性:)
,正如jmdecombe后来正确指出的那样


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

Hi!你有没有想过这件事?到目前为止,对于我来说,属性值似乎没有到达着色器,着色器似乎总是得到值=0.0;这就好像忽略了node.setValue(,forAttribute:)命令一样。可能是iOS错误,还是我遗漏了什么?我希望这不是一只虫子。我希望我遗漏了一些东西。嗨,已经有一段时间了,但我看了一下代码,实际上解决方案是使用一致性而不是属性,然后它就成功了。也就是说,line shader.attributes=etc.必须变成:shader.uniforms=[SKUniform(名称:“a_path_phase”,float:0.0)],然后,行node.setValue等必须变成:node.strokeShader?.uniformNamed(“a_path_phase”)?.floatValue=weak.phase您在这里告诉我的是,您决定写入一个统一而不是一个属性,我没有意识到这是可能的。不过,我们实现需求的两种“替代”方法似乎都证实了一件事,那就是SKShapeNode.setValue(,forAttribute:)中存在一个bug。
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)
    }
}