SCNTechnique clearColor Always Shows sceneBackground When Passes Share Depth Buffer

Problem Description

I'm encountering an issue with SCNTechnique where the clearColor setting is being ignored when multiple passes share the same depth buffer. The clear color always appears as the scene background, regardless of what value I set. The minimal project for reproducing the issue: https://www.dropbox.com/scl/fi/30mx06xunh75wgl3t4sbd/SCNTechniqueCustomSymbols.zip?rlkey=yuehjtk7xh2pmdbetv2r8t2lx&st=b9uobpkp&dl=0

Problem Details

In my SCNTechnique configuration, I have two passes that need to share the same depth buffer for proper occlusion handling:

"passes": [
    "box1_pass": [
        "draw": "DRAW_SCENE",
        "includeCategoryMask": 1,
        "colorStates": [
            "clear": true,
            "clearColor": "0 0 0 0"  // Expecting transparent black
        ],
        "depthStates": [
            "clear": true,
            "enableWrite": true
        ],
        "outputs": [
            "depth": "box1_depth",
            "color": "box1_color"
        ],
    ],
    "box2_pass": [
        "draw": "DRAW_SCENE",
        "includeCategoryMask": 2,
        "colorStates": [
            "clear": true,
            "clearColor": "0 0 0 0"  // Also expecting transparent black
        ],
        "depthStates": [
            "clear": false,
            "enableWrite": false
        ],
        "outputs": [
            "depth": "box1_depth",  // Sharing the same depth buffer
            "color": "box2_color",
        ],
    ],
    "final_quad": [
        "draw": "DRAW_QUAD",
        "metalVertexShader": "myVertexShader",
        "metalFragmentShader": "myFragmentShader",
        "inputs": [
            "box1_color": "box1_color",
            "box2_color": "box2_color",
        ],
        "outputs": [
            "color": "COLOR"
        ]
    ]
]

And the metal shader used to display box1_color and box2_color with splitting:

fragment half4 myFragmentShader(VertexOut in [[stage_in]],
                                texture2d<half, access::sample> box1_color [[texture(0)]],
                                texture2d<half, access::sample> box2_color [[texture(1)]]) {
    half4 color1 = box1_color.sample(s, in.texcoord);
    half4 color2 = box2_color.sample(s, in.texcoord);
    
    if (in.texcoord.x < 0.5) {
        return color1;
    }
    return color2;
};

Expected Behavior

  • Both passes should clear their color targets to transparent black (0, 0, 0, 0)
  • The depth buffer should be shared between passes for proper occlusion

Actual Behavior

  • Both box1_color and box2_color targets contain the scene background instead of being cleared to transparent (see attached image)
  • This happens even when I explicitly set clearColor: "0 0 0 0" for both passes
  • Setting scene.background.contents = UIColor.clear makes the clearColor work as expected, but I need to keep the scene background for other purposes

What I've Tried

  1. Setting different clearColor values - all are ignored when sharing depth buffer
  2. Using DRAW_NODE instead of DRAW_SCENE - didn't solve the issue
  3. Creating a separate pass to capture the background - the background still appears in the other passes
  4. Various combinations of clear flags and render orders

Environment

  • iOS/macOS, running with "My Mac (Designed for iPad)"
  • Xcode 16.2

Question

Is this a known limitation of SceneKit when passes share a depth buffer? Is there a workaround to achieve truly transparent clear colors while maintaining a shared depth buffer for occlusion testing?

The core issue seems to be that SceneKit automatically renders the scene background in every DRAW_SCENE pass when a shared depth buffer is detected, overriding any clearColor settings.

Any insights or workarounds would be greatly appreciated. Thank you!

I was able to reproduce this using your sample project. When scene.background is set, SceneKit renders it in every DRAW_SCENE and DRAW_NODE pass, overriding the clearColor setting. This is a bug — clearColor should be respected per-pass regardless of the scene background.

The workaround is to set scene.background.contents to UIColor.clear and handle the background in your final quad shader instead. We verified this with your project:

In GameViewController:

scene.background.contents = UIColor.clear

Then in your fragment shader, blend the box passes over your desired background:

fragment half4 myFragmentShader(VertexOut in [[stage_in]],
                                texture2d<half, access::sample> box1_color [[texture(0)]],
                                texture2d<half, access::sample> box2_color [[texture(1)]]) {
    half4 color1 = box1_color.sample(s, in.texcoord);
    half4 color2 = box2_color.sample(s, in.texcoord);

    // Replace this with your actual background — a sampled texture, gradient, etc.
    half4 background = half4(0.5, 0.7, 1.0, 1.0);

    if (in.texcoord.x < 0.5) {
        return mix(background, color1, color1.a);
    }
    return mix(background, color2, color2.a);
};

With scene.background cleared, the clearColor: "0 0 0 0" in your passes works correctly and the box outputs have transparent backgrounds. The final quad shader then blends them over the background you provide.

Please file a feedback report through Feedback Assistant with your sample project and post the feedback number here. The underlying issue — scene.background overriding per-pass clearColor in SCNTechnique — needs to be fixed in SceneKit.

SCNTechnique clearColor Always Shows sceneBackground When Passes Share Depth Buffer
 
 
Q