Setting clip shape of a RealityView

I am following this example to create a stereoscopic image: https://developer.apple.com/documentation/visionos/creating-stereoscopic-image-in-visionos

I would also like to add corner radius to the stereoscopic RealityView. With ordinary SwiftUI views, we typically just use .clipShape(RoundedRectangle(cornerRadius: 32)):

struct StereoImage: View {
    var body: some View {
        let spacing: CGFloat = 10.0

        let padding: CGFloat = 40.0

        VStack(spacing: spacing) {
            Text("Stereoscopic Image Example")
                .font(.largeTitle)

            RealityView { content in
                let creator = StereoImageCreator()

                guard let entity = await creator.createImageEntity() else {
                    print("Failed to create the stereoscopic image entity.")
                    return
                }

                content.add(entity)
            }
            .frame(depth: .zero)
        }
        .padding(padding)
        .clipShape(RoundedRectangle(cornerRadius: 32)) // <= HERE!
    }
}

This doesn't seem to actually clip the RealityView shown in the sample above. I am guessing this is due to the fact that the box in the RealityView has a non-zero z scale, which means it isn't on the same "layer" as its SwiftUI containers, and thus isn't clipped by the modifiers apply to the containers.

How can I properly apply a clipshape to RealityViews like this? Thanks!

Hi @NSCruiser,

The current approach won't clip the 3d shape. It's a full on mesh, with vertices and connectivity etc. The 2d clipping approach can't work there (as you guessed above).

There are many ways you might approach this, but the simplest thing I could think of is to use the mesh creation that allows for a corner radius to be specified.

In the StereoImageCreator.swift file around like 67, replace what's there with this:

// Generates the model entity in the shape of a box and applies the shader graph material.
let box = ModelEntity(
  mesh: .generateBox(size: size, cornerRadius: 0.015),
  materials: [material]
)

That does achieve a rounded rect look. If that is not exactly what you are looking for you could try using a plane instead of a box. If you wanted to go with the plane approach the changes are slightly different.

        // Generates the model entity in the shape of a box and applies the shader graph material.
        let box = ModelEntity(
            mesh: .generatePlane(width: size, depth: size, cornerRadius: 0.015),
            materials: [material]
        )
        
        box.transform = Transform(scale: SIMD3<Float>(1.0, 1.0, 1.0),
                                  rotation: simd_quatf(angle: .pi / 2.0, axis: SIMD3<Float>(1.0, 0.0, 0.0)),
                                  translation: SIMD3<Float>(0.0, 0.0, -size / 2.0))

The transform there can be a little confusing. But if you think about it like this: First the mesh is built, its extent is along the x and z axis. With the transform we move it back in z to the center of the plane, then rotate that plane 90° along the x axis. If this is still confusing, please feel free to ask any follow on questions.

I also removed the frame(depth: .zero) line in the StereoImageView.swift modifier attached to the RealityView, around like 41.

With the plane approach and setting the background to orange for visibility I see the attached screen shot.

@Vision Pro Engineer

Thanks for your response. Yes - I figured to add a corner radius to the entity itself, although I used the generatePlane(width: Float, height: Float, cornerRadius: Float = 0) API which seemed to generate a plane along the X-Y plane and eliminated the need to set the transform.

I would also like to add a SwiftUI overlay to the RealityView to display additional information over the image. What's a recommended approach to do this? This has the same problem as the clipShape modifier where the overlay is applied as a 2D view at the "back" of the RealityView and thus isn't visible. One approach I can think of is to put the overlay at the frontmost of the ZStack that contains the RealityView. The ZStack's depth should just be the same (or just a bit more) than the max z scale of the RealityView. However, I am unsure about the best way to calculate this depth value. Can you provide some guidance on this? Thanks!

@Vision Pro Engineer I am also curious as to why the overlay is still covered by the RealityView, now that the RealityView contains only a plane of 0 "thickness" and the overlay is applied above the RealityView on the ZStack.

Setting clip shape of a RealityView
 
 
Q