SKAttributes do not work as expected with SKShapeNode instance

The documentation suggests that it should be possible to use a single shader with multiple instances of an SKNode, such that each instance will use the unique SKAttributes that are passed to it. Let's try that with an SKShapeNode.

This is the fragment shader testFill.fsh, simply coloring based on the value for a_test:

void main()
{
    gl_FragColor = vec4(vec3(a_test), 1.0);
}
And here we make two nodes `testNode0`, and `testNode1`, each using the same shader, but with a different value for `a_test`:
class GameScene: SKScene {
    override func didMove(to view: SKView) {
        let testShader = shaderWithFilename( "testFill", fileExtension: "fsh", uniforms: [])
        testShader.attributes = [
            SKAttribute(name: "a_test", type: .float)
        ]

        let testNode0 = SKShapeNode(rect: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
        testNode0.fillShader = testShader
        testNode0.position = CGPoint(x: -100, y: 300)
        testNode0.setValue(SKAttributeValue(float: 0.2), forAttribute: "a_test")
        addChild(testNode0)

        let testNode1 = SKShapeNode(rect: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
        testNode1.fillShader = testShader
        testNode1.position = CGPoint(x: 100, y: 300)
        testNode1.setValue(SKAttributeValue(float: 0.8), forAttribute: "a_test")
        addChild(testNode1)
    }
}

Here is the result:

The squares are the same color, in particular the result of passing the second value 0.8 for a_test.

Now, let's try the same thing with SKSpriteNode:

class GameScene: SKScene {
    override func didMove(to view: SKView) {
        let testShader = shaderWithFilename( "testFill", fileExtension: "fsh", uniforms: [])
        testShader.attributes = [
            SKAttribute(name: "a_test", type: .float)
        ]

        let testNode0 = SKSpriteNode()
        testNode0.size = CGSize(width: 100.0, height: 100.0)
        testNode0.shader = testShader
        testNode0.position = CGPoint(x: -100, y: 300)
        testNode0.setValue(SKAttributeValue(float: 0.2), forAttribute: "a_test")
        addChild(testNode0)

        let testNode1 = SKSpriteNode()
        testNode1.size = CGSize(width: 100.0, height: 100.0)
        testNode1.shader = testShader
        testNode1.position = CGPoint(x: 100, y: 300)
        testNode1.setValue(SKAttributeValue(float: 0.8), forAttribute: "a_test")
        addChild(testNode1)
    }
}

And it works!


Why does the documentation not say that this is not possible with an SKSpriteNode? Why does an SKSpriteNode have a .setValue method if it does not function as expected?

Is this a bug? Or something that is expected to be obvious?

I am not sure, but I am sharing this in case somebody else ends up stuck on this issue as I was when otherwise trying to do something relatively straightforward.

The solution (if your shape is a simple rect, as it is in my case) is to initialize an empty SKSpriteNode and size it accordingly, after which SKAttributes should work as expected.

Apple, please either fix this, or update the documentation.

Post not yet marked as solved Up vote post of chaimp Down vote post of chaimp
1.3k views

Replies

There does not seem to be a way to edit the post, but that block starting with "And here we make two nodes..." is not supposed to be a code block, fyi.

Same issue here. Any solution for non-rectangular nodes?