A complete Metal or OpenGL shader program that replaces SceneKit's rendering of a geometry or material.
- iOS 8.0+
- macOS 10.8+
- Mac Catalyst 13.0+
- tvOS 9.0+
You use an
SCNProgram object to perform custom rendering using shader programs written in the Metal shading language or the OpenGL Shading Language (GLSL). A program object contains a vertex shader and a fragment shader. To use a program object for rendering, assign it to the
program property of a geometry or material.
Use a program object when you want to completely replace SceneKit’s rendering. Your shaders take input from SceneKit and become responsible for all transform, lighting, and shading effects that you want to produce.
To use a custom shader program in SceneKit, create an
SCNProgram object and optionally specify its
delegate object for handling errors. Next, provide shaders:
Finally, assign your program object to the geometries or materials you want rendered using the shader program.
Rendering with Metal shaders requires that the
rendering property of your
SCNView object (or other renderer) be set to
SCNRendering, which in turn requires that your app be running on Metal-capable hardware. If you provide both Metal and OpenGL shaders in the same
SCNProgram object, SceneKit automatically selects the appropriate shaders to use when rendering, falling back to OpenGL or OpenGL ES shaders when Metal is not supported on the current hardware.
Providing Input to a Metal Shader
Metal shaders for use with SceneKit require an
#include <Scene directive to gain access to SceneKit-specific symbols. Use these symbols to access the kinds of data listed below.
To use vertex attributes provided by
SCNGeometry objects in your shader program, declare those attributes in your Metal shader source code using attribute qualifiers (see Attribute Qualifiers to Locate Per-Vertex Inputs in Metal Shading Language Guide) and the constants listed in Table 1. For example, the partial shader below declares an input structure using the vertex position and normal attributes.
The vertex position, provided by the geometry source for the
The surface normal vector at the vertex, provided by the geometry source for the
The surface-space tangent vector.
SceneKit automatically infers this vector based on texture coordinates. To obtain a bitangent vector, take the cross product of the tangent vector and the surface normal vector, and scale the result by the w component of the tangent vector.
The vertex color, provided by the geometry source for the
Skeletal animation index information, provided by the geometry source for the
Skeletal animation weight information, provided by the geometry source for the
Texture coordinates, provided by the first geometry source for the
Texture coordinates, provided by the second geometry source for the
Texture coordinates, provided by the third geometry source for the
Texture coordinates, provided by the fourth geometry source for the
To use information from SceneKit that is constant for all invocations of your shader when rendering a single frame—such as view and projection matrices, fog parameters, and scene time—declare a parameter to your shader function whose type is
SCNScene, with an attribute qualifier binding it to buffer zero. For example, the shader function declaration below uses scene data in its second parameter.
Your function can then access scene data from the fields in the
SCNScene structure, outlined below.
To use information from SceneKit that varies for each object being rendered with a shader—such as model and normal matrices—declare a parameter to your shader function with an attribute qualifier binding it to buffer one. For the type of this parameter, declare your own struct type containing any of the fields in Listing 4.
For example, the partial shader below declares a struct with model and model-view-projection matrices and uses it in a vertex function.
To use custom input variables in a Metal shader, first declare those variables as input parameters to your Metal shader functions, using an attribute qualifier to bind to buffer 2 (or higher). Because these variables pass to your Metal shader in a buffer, you typically also define a data structure for your variables, as seen in the partial shader below.
There are two options for providing data for your custom variables: manually and at render time.
To make a single change to your custom variable data, use Key-value coding: Call the
setmethod on the geometry or material to be rendered with your shader, passing an
Value(_: for Key:)
NSDataobject containing your data structure as the value and the name of the corresponding shader function parameter as the key. Be aware of layout and alignment when encoding an entire structure as an
NSDataobject—for best results, use data types from the SIMD library (such as
matrix), because those types match the layout and alignment of the GPU data types used in a Metal shader.
In either case, you can alternatively provide a value for a specific member of a structure by wrapping that value in an
NSValueobject and using the fully qualified name of that member as the key. For example, use
colorsas the key in the example above.
To update custom variable data at render time, call the
handlemethod to register a block that SceneKit calls before rendering using your shader program. In the block, SceneKit provides an
Binding(of Buffer Named: frequency: handler:)
writeyou can call to provide a new value for your data structure.
Providing Input to an OpenGL Shader
Vertex attributes. To use vertex attributes provided by
SCNGeometry objects in your shader program, map SceneKit semantics to the input vertex attribute names declared in the shader. Use the
set method and the constants listed in Geometry Semantic Identifiers.
Coordinate transformations. To use the coordinate transformations defined by the scene’s node hierarchy and point-of-view camera in your shader program, map SceneKit’s transform matrices to the uniform variable names declared in the shader. Use the
set method and the constants listed in Rendering Transform Keys.
Custom uniform variables. To provide values for your own custom uniform variables declared in the shader, choose when and how you want to update these values.
To update a value once, use Key-value coding: Call the
setmethod, providing the uniform name from shader source code as the key and an appropriate type of data as the value. To smoothly transition a one-time value change, call the
Value(_: for Key:)
setmethod inside an
Value(_: for Key:)
SCNTransactionanimation or create an animation object with the
init(keymethod, passing the uniform name as the key.
To update a value every time SceneKit renders an object with your shader program, assign binding blocks using the
handlemethod of the geometry or material to be rendered with your custom program. Within a binding block you can execute OpenGL commands to bind shader uniforms or set any other state necessary for rendering.
Binding(of Symbol: handler:)