yojimbo2000 yojimbo2000 - 4 months ago 185x
iOS Question

SceneKit shader modifier for geometry entry points works in iOS but not OS X

I'm in the early stages of making a SceneKit shader modifier (for the geometry entry point) that displaces a plane's geometry according to a height-map texture. The plan is to use it for creating terrain.

In iOS (edit: the iOS Simulator) the shader works as it should, but does print this warning to the console:

SceneKit: error, modifier without code is invalid

How it looks on iOS

When building for OS X however, the shader gets a fatal error, and the terrain geometry just displays as a pink rectangle.

This is the geometry shader modifier:

uniform sampler2D displacementMap;
const float intensity = 7.5;

# pragma body

vec4 displace = texture2D(displacementMap, _geometry.texcoords[0]);

_geometry.position.z += displace.r * intensity;

And this is how the shader is connected to the geometry.
is the height map, it is placed in both the diffuse contents, and the custom
sampler value in the shader modifier:

//displacement shader

let mat = SCNMaterial()
mat.diffuse.contents = terrainScene

var displacementShader: String?
guard let path = NSBundle.mainBundle().pathForResource("DisplaceGeometry", ofType: "shader") else {return}

do {
displacementShader = try String(contentsOfFile: path, encoding: NSUTF8StringEncoding)
} catch {
mat.shaderModifiers = [SCNShaderModifierEntryPointGeometry: displacementShader!]
let noiseProperty = SCNMaterialProperty(contents: terrainScene!)
mat.setValue(noiseProperty, forKey: "displacementMap")

let plane = SCNPlane(width: 80, height: 80)
plane.widthSegmentCount = 40
plane.heightSegmentCount = 40
plane.materials = [mat]

This is the beginning of the OS X error message:

SceneKit: error, modifier without code is invalid
2016-06-11 10:58:32.054 EndlessTerrainOSX[16923:5292387] SceneKit: error, Invalid shader modifier : no code provided
2016-06-11 10:58:32.640 EndlessTerrainOSX[16923:5292387] FATAL ERROR : failed loading compiling shader:

And the end of the error:

UserInfo={NSLocalizedDescription=Compilation failed:
<program source>:343:8: error: global variables must have a constant address space qualifier
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
<program source>:343:19: error: use of undeclared identifier 'displacementMap'
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
<program source>:343:42: error: use of undeclared identifier 'displacementMapSampler'
float4 displace = displacementMap.sample(displacementMapSampler, _geometry.texcoords[0]);
<program source>:344:1: error: unknown type name '_geometry'
_geometry.position.z += displace.r * intensity;
<program source>:344:10: error: cannot use dot operator on a type
_geometry.position.z += displace.r * intensity;
2016-06-11 10:58:32.646 EndlessTerrainOSX[16923:5292387] Shaders without a vertex function are not allowed

Does anyone know why it displays on iOS, but fails on OS X?


I'd suggest it may not be a iOS vs OSX issue, but related to one device using OpenGL and the other Metal.

The code in the error message is Metal, indicating that SceneKit has translated your code from GLSL to Metal. In my experience this doesn't work 100% of the time. In your case the simplest solution may be to force the SCNView to use OpenGL by passing in options when it's created. I believe there's also a dropdown in Interface Builder for this too.

If you wish to keep using Metal, and there are reasons why you would, read on.

When a shader modifier fails to compile the entire contents of the shader is dumped to stdout, this really helps the debugging process. If you scroll up to line 343 you will find that SceneKit has included the float4 displace = displacementMap.sample(...); method before the vertex shader. Being defined outside a function means that it is a global variable, hence requiring the constant address space modifier, but this isn't what you wanted at all... In this instance the translation from GLSL to Metal hasn't worked as you expected.

I note you're missing the #pragma arguments directive, this should be included above the uniform definition (refer to "Writing a Shader Modifier Snippet"), it may help. In my experience I think you'll have a tough time passing a texture into a Metal based shader modifier. I tried, failed, and wrote a SCNProgram to do it instead. Suggest forcing OpenGL.