Methods for customizing SceneKit's rendering of geometry and materials using Metal or OpenGL shader programs.
- iOS 8.0+
- macOS 10.9+
- Mac Catalyst 13.0+
- tvOS 9.0+
- watchOS 3.0+
SceneKit provides two ways to integrate custom GPU shader programs into the rendering of your scene: program objects and shader modifiers.
Use Program Objects to Replace SceneKit Shading
For complete control of the vertex and fragment shaders used to render an object, assign an
SCNProgram instance to the object’s program property. A custom program completely replaces all other rendering parameters, including material settings. Your custom program takes inputs from SceneKit and is responsible for all transform, lighting, and shading effects you want it to produce. For details on custom programs, see
When you specify a custom program, you can provide handler blocks that SceneKit calls at render time to update the values of custom variables in your shaders. See the methods in Handling Parameters in Custom OpenGL Shader Programs.
Use Shader Modifiers to Extend SceneKit Shading
Shader modifiers are an alternative to entirely replacing SceneKit’s shaders with your own. You can use shader modifiers to, for example:
Parametrically deform the surface of a geometry
Simulate realistic surfaces with complex material properties
Add artistic lighting effects such as cartoon-style shading
Create special effects by post-processing pixels after SceneKit’s shading is complete
A shader modifier is a snippet of source code in the Metal shader language or OpenGL Shader Language (GLSL) that SceneKit injects into its own shader programs at a defined entry point. Because your shader modifiers are additions to SceneKit’s shader program, using shader modifiers leaves SceneKit’s rendering system intact. That is, you can still use material properties, lighting models, and camera effects to affect the rendered image.
You attach a snippet to a
SCNMaterial object using its
shader property, associating it with an entry point corresponding to the stage of SceneKit’s shader program that it modifies: geometry, surface, lighting, or fragment. Each entry point defines a context for the associated snippet, with input variables providing SceneKit’s rendering parameters in that stage and output variables that the snippet writes its results to.
For definitions of each entry point and its inputs and outputs, see Shader Modifier Entry Point Keys. SceneKit inserts your shader modifiers into its shader program a specific order:
You can use the structures defined by earlier entry points in later entry points. For example, a snippet associated with the
fragment entry point can read from the
_surface structure defined by the
surface entry point.
Writing a Shader Modifier Snippet
The code you provide for a shader modifier must be organized in a specific structure, as illustrated in the example below:
Custom variables declarations. You can provide your own inputs to a shader modifier by declaring custom variables. Because the syntax differs between Metal and OpenGL shaders, you must include both declarations for your shader modifier to be usable with both rendering technologies. To pass values into custom variables at render time, see Providing Custom Inputs to a Shader Modifier.
Custom global functions. If your shader modifier benefits from factoring common code into functions, place their definitions here. If you include custom functions in your snippet, you must place the
#pragma bodydirective between your function definitions and the main body of the snippet.
Pragma directives. As noted above, the
#pragma bodydirective separates custom function definitions from the main body of the snippet. If the snippet contains no function definitions, you may omit this directive.
By default, SceneKit automatically uses material properties to determine whether an object should be rendered with partial transparency and uses this information to optimize rendering performance. Use the
#pragma opaquedirective to override SceneKit’s setting.
Code Snippet. Place the main body of your shader modifier code at the end of the code snippet.
One shader modifier snippet affects both Metal and OpenGL (or OpenGL ES) rendering—SceneKit automatically translates GLSL syntax to Metal shader syntax before inserting your code snippet into its own shader program. (For some simple shader modifiers, SceneKit can insert the same code snippet into either shader language without translation.)
Providing Custom Inputs to a Shader Modifier
You can also declare custom uniform variables in your shader modifier snippet. You provide values for custom uniform variables using Key-value coding. For each uniform variable you declare in a shader modifier attached to an
SCNGeometry object, SceneKit observes a key with the same name on that object. When you set a new value, SceneKit automatically binds that value to the corresponding uniform location in the shader program. If you animate a change to the value implicitly (with the
SCNTransaction class) or explicitly (with the
SCNAnimatable protocol), SceneKit interpolates intermediate values and binds them to the uniform for each frame of the animation. For example, the following code animates the fading of a material from full color to grayscale:
Because SceneKit binds values to shader variables using Key-value observing, you can provide values in several ways. If you set a value using the
set method, or set a target value for a keypath animation, the value must be contained in an Objective-C object. For uniforms of scalar types, you can assign an
NSNumber object as a value. Assigning a uniform of a vector or matrix type requires an
NSValue object containing data of the appropriate type. You can also bind textures to texture samplers using
Alternatively, you can create an
SCNGeometry subclass for your custom shadable object and declare properties whose names match those of the uniform variables in your shader. When you assign a value to the property, SceneKit automatically binds it to the corresponding uniform in the shader program. In this case, your properties can use primitive or structure types appropriate to the corresponding Metal or GLSL variables.
The table below lists the Objective-C types for each shading language data type:
GLSL data types
Metal data types
Using Inputs Provided by SceneKit
SceneKit declares the following uniform variables containing global rendering parameters:
The current system time (in seconds) since SceneKit started rendering with the shader.
The bounding box of the geometry being rendered, in model space.
The transform matrices used for converting vertex positions and normals between model, world, view, and clip coordinate spaces.
For detailed definitions, see Rendering Transform Keys.
The inverse matrices corresponding to each transform.
The texture contents of the corresponding material property. Declared only if the material property’s
The GLSL type of the uniform variable depends on whether the contents are a 2D image or cube map.
For details on materials, see
Shader modifiers may contain any legal Metal shader or GLSL code, with the exception that SceneKit reserves for its own use all identifier names with the