2

this is the surface shader which I use to make a trail on floor surface.

#pragma arguments
uniform vec2 trailPoints[5];
uniform float count;

#pragma body
float trailRadius = 10.0;

float x = _surface.diffuseTexcoord.x;
float x100 = float(x * 100);

float y = _surface.diffuseTexcoord.y;
float y100 = float(y * 100);

for (int i = 0; i < int(count); i++) {
    vec2 position = trailPoints[i];
    if ((x100 > position.x - trailRadius && x100 < position.x + trailRadius) && (y100 > position.y - trailRadius && y100 < position.y + trailRadius)) {
        _surface.diffuse.rgb = vec3(0.0, 10.0 ,0.0);
    }
}

and this is the swift side code which I use to pass vector data to surface shader.

            if let geometry = self.floorNode.geometry {
                if let material = geometry.firstMaterial {

                    // this is the temporary data which I use to find the problem.
                    // this data will be dynamic later on.
                    let myValueArray:[float2] = [float2(x:80, y:80),float2(x:60, y:60),float2(x:40, y:40),float2(x:20, y:20),float2(x:0, y:0)]

                    // Passing array count to shader. There is no problem here.
                    var count = Float(myValueArray.count)
                    let countData = Data(buffer: UnsafeBufferPointer(start: &count, count: 1))
                    material.setValue(countData, forKey: "count")

                    // and here is the problem start. 
                    // myValueArray converted to data with its size.
                    let valueArrayData = Data(buffer: UnsafeBufferPointer(start: myValueArray, count: myValueArray.count))
                    material.setValue(valueArrayData, forKey: "trailPoints")                    
                }                
            }

When I build and run the project the following error occurred and no data passed to the "trailPoints" in shader.

Error: arguments trailPoints : mismatch between the NSData and the buffer size 40 != 8

When I change the array count to 1 while converting array to data,

let valueArrayData = Data(buffer: UnsafeBufferPointer(start: myValueArray, count: 1))

the errors dissapear but only the first member of the array will passing to shader.

so, the problem is,

how can I pass the all array members to the shader?

5
  • Doesn't count: myValueArray.count need to be count: myValueArray.count * sizeof(float2)? Commented Jan 18, 2017 at 13:12
  • 1
    @solidpixel No, all is good here. It seems that this high level API does not support uniform arrays. The NSData object contains 40 bytes (which is 10 32bit floats) but the error seems to come from some key value observing which reports that it wants 8 bytes where I can only assume it saved the trailPoints as vec2 so 2 floats (32bit) is 8 bytes. I would never consider using this API over the pure openGL but as far as I looked into it there are no methods to manipulate the shader input on a lower level. Commented Jan 18, 2017 at 13:20
  • Thanks Matic. I suspected the reason that you mentioned but I was not sure. Apparently there is no chance to pass to array to shader with using Shader Modifier Entry Points in Scenekit. I'll try SCNProgram with a pure hope. Commented Jan 18, 2017 at 14:50
  • The GLES API does support per element upload, but I'm not sure if it is exposed via the swift support. Can you do component-wise upload by iterating over the array and using material.setValue(valueArrayData, forKey: "trailPoints[0]"), then material.setValue(valueArrayData, forKey: "trailPoints[1]"), etc? Commented Jan 18, 2017 at 19:14
  • Thanks solidpixel. I saw something like your advice on the internet previously. It looks a little bit ugly, but I tried once again and nothing changed. And I have also tried this in a way that seems more "sensible" like this, material.setValue(NSValue(cgPoint: CGPoint(x: 80, y: 80)), forKey: "trailPoints[0]") material.setValue(NSValue(cgPoint: CGPoint(x: 60, y: 60)), forKey: "trailPoints[1]")...etc No. Did not work. Commented Jan 18, 2017 at 21:38

1 Answer 1

1

I think, the answer of this question this:

I recently realized, the OpenGl ES 2.0 only allow the following array definitions:

float myValue[3];
myValue[0] = 1.0;
myValue[1] = 2.0;
myValue[2] = 3.0;

But as far as I can tell, it is not possible to do this using the SCNShaderModifierEntryPoint with following way.

material.setValue (1.0, forKey: "myValue[0]")
material.setValue (2.0, forKey: "myValue[1]")
material.setValue (3.0, forKey: "myValue[2]")

And finally I found a way to pass the array to fragment shader with SCNProgram handleBinding method.

let myValue:[Float] = [1.0, 2.0, 3.0]
material.handleBinding(ofSymbol:"myValue", handler: { (programId:UInt32, location:UInt32, node:SCNNode?, renderer:SCNRenderer) in
       for (index, v) in myValue.enumerated() {
            var v1 = v
            let aLoc = glGetUniformLocation(programId, String(format: "myValue[%i]", index))
            glUniform1fv(GLint(aLoc), 1, &v1)
       }
 })

But, SCNProgram is completly rid off the default swift shader program and use yours. The default shader program of swift is highly complex and do lots of things to your place.

default vertex shader of swift

default fragment shader of swift

So maybe its not a good idea to use SCNProgram for only pass the arrays to shader.

And one interesting thing, SCNProgram does not work on SCNFloor geometry.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.