2

I'm trying to pass in an array of points into a shader modifier in SceneKit, but I can't work out the correct syntax. In my shader extension code I have:

uniform vec2 points[100];

If I call…

material.setValue(NSValue(point: CGPoint(x: 100.5, y: 50.5)), forKey: "points")

…then it sets the value of points[0], which makes me think that maybe it's not possible. I've tried lots of other combinations for both the key and the value, but nothing seems to work.

Is there a better way to do this? My end goal is to modify the surface diffuse colour for a set of points in the array, and use the default rendering otherwise. Is there a better way to do this in the shader than looping over an array of vec2s?

Thanks for your help.

Luke Rogers
  • 2,369
  • 21
  • 28

1 Answers1

1

A shader array variable is just a blob of data passed from the GL client to the GPU, with some size and layout agreed upon in the shader code.

So, to do this in SceneKit, you'll need to first set up your blob, then wrap it in an NSData to pass to the shader. e.g.

// warning: coded in Safari and untested

import simd
let points: [float2] = [[100.5, 50.5], [110.5, 60.5], /* ... */ ]
// make sure you have 100...
let data = NSData(bytes: points, length: points.count * sizeof(float2))
material.setValue(data, forKey: "points")

Also, when you're working with single points, SceneKit will handle the layout conversion from CGPoint to GPU vec2, but when you pass a blob of data, all SceneKit knows is that it's a blob of data... it can't look inside and adjust the layout for what the GPU might expect, because it doesn't know what the layout you provided is.

So, you should use types that explicitly match the size and layout that you want in the GPU shader. That means no CGPoint—its components are CGFloat, and that type's size changes between CPU architectures. The float2 type from the SIMD library is a close match for GL vec2 and an exact match for Metal float2.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • Thanks @rickster. I'm having some issues with this though — firstly, it seems that SIMD is incomplete in Swift 1.2, so I don't have the `float2` type. So I tried making a one dimensional array of `GLfloat`s and changing the uniform to have a `float[]` type, then created the data and tried to set the value as above, but even that had no effect (the shader renders as if all the values are 0). Any idea as to what could be going wrong? – Luke Rogers Jun 18 '15 at 12:49
  • SIMD types are available in Swift only in Swift 2.0 (currently beta, with Xcode 7 beta). Using a `float` array is fine (and you don't need to change the shader declaration, because an array of 200 `float`s is the same layout as an array of 100 `vec2`s). Not sure about the `NSData`-to-shader part failing, though... will look into that some more. – rickster Jun 19 '15 at 19:38
  • @rickster Did you ever find out why it does not work? I also have the same problem, where the values in the shader does not seem to be set – A. Claesson Jul 06 '21 at 07:18