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.