Repeating a texture over a plane in SceneKit

前端 未结 4 1133
眼角桃花
眼角桃花 2021-02-05 17:05

I have a 32x32 .png image that I want to repeat over a SCNPlane. The code I\'ve got (See below) results in the image being stretched to fit the size of the plane, rather than re

4条回答
  •  太阳男子
    2021-02-05 17:20

    I faced an identical issue when implementing plane visualisation in ARKit. I wanted to visualise the detected plane as a checkerboard pattern. I fixed it by creating a custom SCNNode called a "PlaneNode" with a correctly configured SCNMaterial. The material uses wrapS, wrapT = .repeat and calculates the scale correctly based on the size of the plane itself.

    Looks like this:

    Have a look at the code below, the inline comments contain the explanation.

    class PlaneNode : SCNNode {
    
        init(planeAnchor: ARPlaneAnchor) {
            super.init()
            // Create the 3D plane geometry with the dimensions reported
            // by ARKit in the ARPlaneAnchor instance
            let planeGeometry = SCNPlane(width:CGFloat(planeAnchor.extent.x), height:CGFloat(planeAnchor.extent.z))
            // Instead of just visualizing the grid as a gray plane, we will render
            // it in some Tron style colours.
            let material = SCNMaterial()
            material.diffuse.contents = PaintCode.imageOfViewARPlane
            //the scale gives the number of times the image is repeated
            //ARKit givest the width and height in meters, in this case we want to repeat
            //the pattern each 2cm = 0.02m so we divide the width/height to find the number of patterns
            //we then round this so that we always have a clean repeat and not a truncated one
            let scaleX = (Float(planeGeometry.width)  / 0.02).rounded()
            let scaleY = (Float(planeGeometry.height) / 0.02).rounded()
            //we then apply the scaling
            material.diffuse.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
            //set repeat mode in both direction otherwise the patern is stretched!
            material.diffuse.wrapS = .repeat
            material.diffuse.wrapT = .repeat
            //apply material
            planeGeometry.materials = [material];
            //make a node for it
            self.geometry = planeGeometry
            // Move the plane to the position reported by ARKit
            position.x = planeAnchor.center.x
            position.y = 0
            position.z = planeAnchor.center.z
            // Planes in SceneKit are vertical by default so we need to rotate
            // 90 degrees to match planes in ARKit
            transform =  SCNMatrix4MakeRotation(-Float.pi / 2.0, 1.0, 0.0, 0.0);
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func update(planeAnchor: ARPlaneAnchor) {
            guard let planeGeometry = geometry as? SCNPlane else {
                fatalError("update(planeAnchor: ARPlaneAnchor) called on node that has no SCNPlane geometry")
            }
            //update the size
            planeGeometry.width = CGFloat(planeAnchor.extent.x)
            planeGeometry.height = CGFloat(planeAnchor.extent.z)
            //and material properties
            let scaleX = (Float(planeGeometry.width)  / 0.02).rounded()
            let scaleY = (Float(planeGeometry.height) / 0.02).rounded()
            planeGeometry.firstMaterial?.diffuse.contentsTransform = SCNMatrix4MakeScale(scaleX, scaleY, 0)
            // Move the plane to the position reported by ARKit
            position.x = planeAnchor.center.x
            position.y = 0
            position.z = planeAnchor.center.z
    
        }
    }
    

提交回复
热议问题