Demonstrates how to use layer selection to reduce the number of render passes needed for a reflective object.
- iOS 12.0+
- macOS 10.13+
- Xcode 11.0+
This sample demonstrates dynamic reflections on a chrome sphere, using layer selection to render the frame in two passes. The first pass renders the environment onto the cube map. The second pass renders the environment reflections onto the sphere; it renders additional actors in the scene; and it renders the environment itself.
You can implement an object that reflects its environment by sampling its reflections from a cube map of the environment. A cube map is a single texture composed of six 2D texture layers arranged in the shape of a cube. The reflections vary based on the positions of other objects in the environment, so each of the cube map’s six faces must be rendered dynamically in every frame. This would normally require six separate render passes, one for each face, but Metal allows you to render an entire cube map in a single pass.
This sample contains macOS and iOS targets. Run the iOS scheme on a physical device because Metal isn’t supported in the simulator.
Layer Selection is supported on all macOS GPUs but only iOS GPUs which support the
MTLFeature feature set.
You check the GPU that you choose at runtime if it supports ICBs using
This sample calls ‘supportsFeatureSet:’ for this purpose within its view controller’s
Separate the Scene
A cube map is represented as a render target array with six layers, one for each of its faces. The
[[render attribute qualifier, specified for a structure member of a vertex function return value, identifies each array layer separately. This layer selection feature allows the sample to decide which part of the environment gets rendered to which cube map face.
AAPLActor object represents an actor in the scene. In this sample, each actor is a temple model with the same mesh data but a different diffuse color. These actors sit on the XZ-plane; they’re always reflected in the X or Z direction relative to the sphere and could be rendered to any of the +X, -X, +Z, or -Z faces of the cube map.
Perform Culling Tests for the Reflection Pass
Before rendering to the cube map, it’s useful to know which faces each actor should be rendered to. Determining this information involves a procedure known as a culling test, and it’s performed on each actor for each cube map face.
At the start of every frame, for each cube map face, a view matrix is calculated and the view’s frustum is stored in the
These culler probes test the intersection between an actor and the viewing frustum of each cube map face. The test results determine how many faces the actor is rendered to (
instance) in the reflection pass, and which faces (
instance) it’s rendered to.
The following diagram shows the results of the culling tests performed on the temple actors, based on their positions relative to the reflective sphere. Because
actor bisect two viewing frustums, their
instance property is set to 2, and there are two elements in their
instance array. (This array contains the cube map face indices of the viewing frustums that the actors intersect.)
Configure Render Targets for the Reflection Pass
The render target for the reflection pass is a cube map. The sample configures the render target by using a
MTLRender object with a color render target, a depth render target, and six layers. The
render property sets the number of cube map faces and allows the render pipeline to render into any or all of them.
Issue Draw Calls for the Reflection Pass
draw method sets up the graphics rendering state for each actor. Actors are only drawn if they are visible in any of the six cube map faces, determined by the
visible value (accessed through the
instance property). The value of
visible determines the number of instances for the instanced draw call.
In this draw call, the sample sets the
base parameter to the value of
actor. This setting is important because it tells the vertex function how to select the appropriate render target layer for each instance.
Render the Reflection Pass
vertex vertex function, the
instance argument points to the buffer that contains the cube map faces that each actor should be rendered to. The
instance value indexes into the
The output structure of the vertex function,
Color, contains the
face member that uses the
[[render attribute qualifier. The return value of
face determines the cube map face that the render pipeline should render to.
Because the value of the draw call’s
base parameter is set to
instance value of the first instance drawn in the draw call is equal to this value. Each subsequent rendering of an instance increments the
instance value by 1. The
instance array has five slots for each actor because an actor can be visible in up to five cube map faces. As a result, the
instance element always contains one of the face indices in which the actor is visible. Therefore, the sample uses this value to select a valid render target layer.
In summary, to render each actor to the reflective cube map, the sample issues an instanced draw call for the actor. The vertex function uses the built-in
instance variable to index into the
instance array that contains the index of the cube map face that the instance should be rendered to. Therefore, the vertex function sets this face index in the
face return value member, which uses the
[[render attribute qualifier. This ensures that each actor is rendered to each cube map face it should appear in.
Perform Culling Tests for the Final Pass
The sample performs similar view updates for the main camera in the final pass. At the start of every frame, a view matrix is calculated and the view’s frustum is stored in the
This final culler probe is used to test the intersection between an actor and the viewing frustum of the camera. The test result simply determines whether or not each actor is visible in the final pass.
Configure Render Targets for the Final Pass
The render target for the final pass is the view’s drawable, a displayable resource obtained by accessing the view’s
current property. However, you must not access this property prematurely because it implicitly retrieves a drawable. Drawables are expensive system resources created and maintained by the Core Animation framework. You should always hold a drawable as briefly as possible to avoid resource stalls. In this sample, a drawable is acquired just before the final render pass is encoded.
Issue Draw Calls for the Final Pass
draw: method sets up the graphics rendering state for each actor. Actors are only drawn if they are visible to the main camera, as determined by the
visible value (accessed through the
Because each actor is drawn only once in the final pass, the
instance parameter is always set to 1 and the
base parameter is always set to 0.
Render the Final Pass
The final pass renders the final frame directly to the view’s drawable, which is then presented onscreen.