ARKit中UIWebView显示异常及SKVideoNode卡顿问题求助
Hey there, let's tackle your two AR-related problems step by step. Both issues stem from specific quirks in how SceneKit interacts with UIKit/SpriteKit components, so let's break them down:
1. UIWebView/WKWebView Not Displaying Correctly Post-Image Detection
First off, UIWebView is deprecated—so switching to WKWebView is non-negotiable, but there are a few key mistakes in your current implementation that are causing the problem:
What's Going Wrong:
- You're adding the web view node to
rootNodeinstead of the tracked image's node, meaning it won't stay anchored to the detected image. - The
xOffsetvariable is undefined in your code snippet—this is likely causing the plane to have an invalid size, leading to unexpected rendering. - Even with
DispatchQueue.main.async, WKWebView requires proper configuration and size matching between the UI component and the SceneKit plane.
Fixed WKWebView Implementation:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let imageAnchor = anchor as? ARImageAnchor else { return } updateQueue.async { let physicalWidth = imageAnchor.referenceImage.physicalSize.width let physicalHeight = imageAnchor.referenceImage.physicalSize.height // Create detection highlight plane let mainPlane = SCNPlane(width: physicalWidth, height: physicalHeight) mainPlane.firstMaterial?.colorBufferWriteMask = .alpha let mainNode = SCNNode(geometry: mainPlane) mainNode.eulerAngles.x = -.pi / 2 mainNode.renderingOrder = -1 node.addChildNode(mainNode) self.highlightDetection(on: mainNode, width: physicalWidth, height: physicalHeight, completionHandler: { DispatchQueue.main.async { // Define a pixel size for the web view (match your desired resolution) let webViewPixelSize = CGSize(width: 800, height: 1360) // Calculate plane size relative to the tracked image (adjust ratio as needed) let webPlaneWidth = physicalWidth * 1.1 let webPlaneHeight = webPlaneWidth * (webViewPixelSize.height / webViewPixelSize.width) // Initialize WKWebView with proper configuration let webConfig = WKWebViewConfiguration() let webView = WKWebView(frame: CGRect(origin: .zero, size: webViewPixelSize), configuration: webConfig) webView.load(URLRequest(url: URL(string: "https://www.facebook.com/")!)) // Create plane for web view let webViewPlane = SCNPlane(width: webPlaneWidth, height: webPlaneHeight) webViewPlane.cornerRadius = 0.05 // Adjust to a reasonable physical size (not too large) webViewPlane.firstMaterial?.diffuse.contents = webView webViewPlane.firstMaterial?.isDoubleSided = false // Create node and anchor it to the detected image's node let webViewNode = SCNNode(geometry: webViewPlane) webViewNode.position.z = -0.01 // Slightly in front of the detection plane webViewNode.opacity = 0 // Add to the tracked image's node, not rootNode node.addChildNode(webViewNode) // Run your animation sequence webViewNode.runAction(.sequence([ .wait(duration: 3.0), .fadeOpacity(to: 1.0, duration: 1.5), .moveBy(x: webPlaneWidth * 1.1, y: 0, z: -0.05, duration: 1.5), .moveBy(x: 0, y: 0, z: -0.05, duration: 0.2) ])) } }) } }
Key Fixes:
- Anchored the web view node to the tracked image's
nodeinstead ofrootNodeto keep it aligned with the detected image. - Defined a clear pixel size for WKWebView and matched the SCNPlane's physical size to maintain aspect ratio.
- Used a reasonable
cornerRadiusvalue (0.05m instead of 0.25m) to avoid excessive rendering overhead. - Removed unnecessary double-sided rendering for better performance.
2. SKVideoNode Glitches (Lag & Pixelation) with cornerRadius
The issue here is that SCNPlane.cornerRadius is computationally expensive for real-time content like video—especially when the radius is large relative to the plane size. This causes frame drops and rendering artifacts.
What's Going Wrong:
- A
cornerRadiusof 0.25m is extremely large for most tracked images (e.g., a standard postcard is ~0.15m wide), forcing SceneKit to do heavy real-time masking. - Mismatched SKScene and video resolution can also contribute to pixelation and lag.
Fixed Video Node Implementation (Using Masking for Rounded Corners):
Instead of relying on SCNPlane.cornerRadius, use a mask node to create rounded corners without performance hits:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let imageAnchor = anchor as? ARImageAnchor else { return } updateQueue.async { let physicalSize = imageAnchor.referenceImage.physicalSize // Create detection highlight plane let mainPlane = SCNPlane(width: physicalSize.width, height: physicalSize.height) mainPlane.firstMaterial?.colorBufferWriteMask = .alpha let mainNode = SCNNode(geometry: mainPlane) mainNode.eulerAngles.x = -.pi / 2 mainNode.renderingOrder = -1 node.addChildNode(mainNode) self.highlightDetection(on: mainNode, width: physicalSize.width, height: physicalSize.height, completionHandler: { // Initialize video node based on anchor name var videoNode: SKVideoNode! switch imageAnchor.name { case "Elephant-PostCard": videoNode = SKVideoNode(fileNamed: "Movies/cat.mp4") case "puppy": videoNode = SKVideoNode(fileNamed: "puppy.mov") default: return // Exit if no matching video } // Configure video videoNode.yScale = -1.0 videoNode.play() // Match SKScene size to video resolution guard let videoAsset = videoNode.videoAsset else { return } let videoSize = CGSize(width: videoAsset.naturalSize.width, height: videoAsset.naturalSize.height) let videoScene = SKScene(size: videoSize) videoScene.anchorPoint = CGPoint(x: 0.5, y: 0.5) videoScene.addChild(videoNode) // Create main video plane let videoPlane = SCNPlane(width: physicalSize.width, height: physicalSize.height) videoPlane.firstMaterial?.diffuse.contents = videoScene videoPlane.firstMaterial?.isDoubleSided = false let videoPlaneNode = SCNNode(geometry: videoPlane) // Create rounded mask plane let maskPlane = SCNPlane(width: physicalSize.width, height: physicalSize.height) maskPlane.cornerRadius = 0.05 // Use reasonable radius maskPlane.firstMaterial?.colorBufferWriteMask = .alpha maskPlane.firstMaterial?.diffuse.contents = UIColor.white let maskNode = SCNNode(geometry: maskPlane) // Apply mask to video node videoPlaneNode.maskNode = maskNode // Add to the main detection node mainNode.addChildNode(videoPlaneNode) }) } }
Key Fixes:
- Used a separate mask node to create rounded corners—this is far more performant than applying
cornerRadiusdirectly to a video-backed plane. - Matched the SKScene size to the video's natural resolution to eliminate scaling artifacts.
- Removed double-sided rendering to reduce GPU load.
- Used a smaller, more realistic
cornerRadiusvalue (0.05m) for better performance.
内容的提问来源于stack exchange,提问作者jack87

