I've struggling with this question too, and I've made a recent discovery that blew this thing wide open for me. It has aided me tremendously in grasping how SceneKit, shaders, shader modifiers and OpenGL/Metal work together and what tools/methods I have available when writing a shader modifier.
Furthermore, I believe the currently accepted answer is no longer correct (fortunately! 🎉).
SceneKit provides default vertex and fragment Metal shaders. It is into these shaders that the shader modifiers are injected.
You can find these default Metal shaders when you capture a GPU frame in Xcode.
Double click on either commonprofile_vert
or commonprofile_frag
(both shaders are inside the same "file", as demonstrated by the same values in the Details column).
There is a lot of information in this file, and it demonstrates some of the inner workings of how SceneKit talks to the GPU when rendering. This is an example of the file (although this version is a couple of years old already it is still exemplary).
Notice how the following lines are injection places for the shader modifiers.
// DoLightModifier START
// DoLightModifier END
...
// DoGeometryModifier START
// DoGeometryModifier END
...
// DoSurfaceModifier START
// DoSurfaceModifier END
...
// DoFragmentModifier START
// DoFragmentModifier END
You will see that your shader modifiers have been placed here.
Look for #ifdef USE_EXTRA_VARYINGS
. Any varying
variables you declare in the geometry modifier show up as property on commonprofile_io
structs. Of which you use the following instance: commonprofile_io out;
.
This file also demonstrates why you need varyings
for communication between the geometry and fragment shader. And why _surface
is accessible in the fragment shader: the fragment modifier and surface modifier have the same scope.
I am absolutely stumped how none of this stuff is documented or even hinted at by Apple, save for some tucked away WWDC-slides probably. I have no idea how people without a couple of weeks of spare time are supposed to figure out how it all works.