首页 > 解决方案 > 如何绘制线节点在相机中保持与 iPhone 中的 Measure App 相同的大小?

问题描述

我尝试将 AR 应用程序作为 iPhone 中的 Measure 默认应用程序。(我基于github上的项目TBXark/Ruler)

我绘制 startNode、endNode、圆柱线和 SCNText。但是我无法管理尺寸的比例,它只能在近处读取,而在测量远平面检测时则很小。

我有两个问题:

  1. 与Measure App接近或远距离时如何保持尺寸节点,圆柱体和文本相同

  2. 如何用背景绘制 scntext 并与 Measure App 对齐相同方向的圆柱线。

这是我的线节点类:

class LineNode: NSObject {

let startNode: SCNNode
let endNode: SCNNode
var lineNode: SCNNode?
let textNode: SCNNode
let sceneView: ARSCNView?

// init func
init(startPos: SCNVector3,
     sceneV: ARSCNView,
     color: (start: UIColor, end: UIColor) = (UIColor(hexCss: 0xF1B426), UIColor(hexCss: 0xD43278)),
     font: UIFont = UIFont.boldSystemFont(ofSize: 8) ) {
    sceneView = sceneV

    let scale = 1 / 400.0
    let scaleVector = SCNVector3(scale, scale, scale)

    func buildSCNSphere(color: UIColor) -> SCNSphere {
        let dot = SCNSphere(radius: 1)
        dot.firstMaterial?.diffuse.contents = color
        dot.firstMaterial?.lightingModel = .constant
        dot.firstMaterial?.isDoubleSided = true
        return dot
    }
    // startNode
    startNode = SCNNode(geometry: buildSCNSphere(color: color.start))
    startNode.scale = scaleVector
    startNode.position = startPos
    sceneView?.scene.rootNode.addChildNode(startNode)

    // endNode
    endNode = SCNNode(geometry: buildSCNSphere(color: color.end))
    endNode.scale = scaleVector
    // line with start to end
    lineNode = CylinderLine(parent: sceneView!.scene.rootNode,
                            v1: startNode.position,
                            v2: endNode.position,
                            radius: 0.001,
                            radSegmentCount: 16,
                            color: UIColor.white)

    sceneView?.scene.rootNode.addChildNode(lineNode!)
    // text show measure line length
    let text = SCNText (string: "--", extrusionDepth: 0.1)
    text.font = font
    text.firstMaterial?.diffuse.contents = UIColor(hexCss: 0xffa800)
    text.firstMaterial?.lightingModel = .constant
    text.alignmentMode = CATextLayerAlignmentMode.center.rawValue
    text.truncationMode = CATextLayerTruncationMode.middle.rawValue
    text.firstMaterial?.isDoubleSided = true
    textNode = SCNNode(geometry: text)

    textNode.scale = SCNVector3(1 / 500.0, 1 / 500.0, 1 / 500.0)

    super.init()
}

// update end node realtime
public func updatePosition(pos: SCNVector3, camera: ARCamera?, unit: MeasurementUnit.Unit = MeasurementUnit.Unit.centimeter) -> Float {
    // update endNode
    let posEnd = updateTransform(for: pos, camera: camera)
    if endNode.parent == nil {
        sceneView?.scene.rootNode.addChildNode(endNode)
    }
    endNode.position = posEnd
    // caculate new mid
    let posStart = startNode.position
    let middle = SCNVector3((posStart.x + posEnd.x) / 2.0, (posStart.y + posEnd.y) / 2.0 + 0.002, (posStart.z + posEnd.z) / 2.0)
    // update text measure
    let text = textNode.geometry as! SCNText
    let length = posEnd.distanceFromPos(pos: startNode.position)
    text.string = MeasurementUnit(meterUnitValue: length).roundUpstring(type: unit)
    text.materials.first?.diffuse.contents = UIColor.orange
    textNode.setPivot()
    textNode.position = middle
    if textNode.parent == nil {
        sceneView?.scene.rootNode.addChildNode(textNode)
    }

    lineNode?.removeFromParentNode()
    lineNode = lineBetweenNodeA(nodeA: startNode, nodeB: endNode)
    sceneView?.scene.rootNode.addChildNode(lineNode!)

    return length
    }
}

拉远时线节点很小

在此处输入图像描述

标签: swiftaugmented-realityarkitmeasure

解决方案


我用它来更新比例,即使你离得很远它仍然可读

func updateScaleFromCameraForNodes(_ nodes: [SCNNode], fromPointOfView pointOfView: SCNNode , useScaling: Bool){
        nodes.forEach { (node) in

            //1. Get The Current Position Of The Node
            let positionOfNode = SCNVector3ToGLKVector3(node.worldPosition)

            //2. Get The Current Position Of The Camera
            let positionOfCamera = SCNVector3ToGLKVector3(pointOfView.worldPosition)

            //3. Calculate The Distance From The Node To The Camera
            let distanceBetweenNodeAndCamera = GLKVector3Distance(positionOfNode, positionOfCamera)

            let a = distanceBetweenNodeAndCamera*1.75
            if(useScaling) {
                node.simdScale = simd_float3(a,a,a)

            }

        }
        SCNTransaction.flush()
    }

然后在渲染器 updateAtTime 中调用它

self.updateScaleFromCameraForNodes(self.nodesAdded, fromPointOfView:
self.cameraNode, useScaling: true)

推荐阅读