1
votes

I am trying to determine the width in world coordinate space at different Z depths of an SCNCamera. As I understand it, a Z value of 0 is the near plane and 1 the far plane.

Again, my understanding is that the frustum is an inverted square pyramid whose apex is at the SCNCamera.

What is throwing me off is that the widths I am obtaining are not linear but seem exponential as I get closer to Z = 1. To me, this is contrary to the mental shape I make of the frustum as a square-based pyramid.

Here is sample playground code:

import UIKit
import SceneKit
import PlaygroundSupport

let scene = SCNScene()
let leftCamera = SCNCamera()
let leftCameraNode = SCNNode()

let boxGeometry = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 1.0)
let box = SCNNode(geometry: boxGeometry)
box.position = SCNVector3(0,0,0)
scene.rootNode.addChildNode(box)

scene.rootNode.addChildNode(leftCameraNode)
leftCameraNode.position = SCNVector3(0, 0, 50)
leftCameraNode.eulerAngles = SCNVector3(0, 0, 0)
leftCameraNode.camera = leftCamera

let leftCameraView = SCNView(frame: CGRect(x: 0, y: 0, width: 700, height: 500))
PlaygroundPage.current.liveView = leftCameraView

leftCameraView.scene = scene
leftCameraView.autoenablesDefaultLighting = true
leftCameraView.pointOfView = leftCameraNode

var widths = [CGFloat : Float]()

func widthForDepth(_ depth: CGFloat) {
    let topLeftUnprojectedPoint = leftCameraView.unprojectPoint(SCNVector3(0, 0, depth))
    let topRightUnprojectedPoint = leftCameraView.unprojectPoint(SCNVector3(leftCameraView.bounds.width, 0, depth))
    let width = topRightUnprojectedPoint.x - topLeftUnprojectedPoint.x

    widths[depth] = width
}

widthForDepth(0)
widthForDepth(0.5)
widthForDepth(0.75)
widthForDepth(0.9)
widthForDepth(0.95)
widthForDepth(1)

This gets me the following values at different depths: Z=0 -> 1.61

Z=0.5 -> 3.2

Z=0.75 -> 6.2

Z=0.9 -> 14.8

Z=0.95 -> 27.1

Z=1 -> 161.6

My understanding is that this value for Z=1 is correct, however other values don't seem to make sense to me.

My Box is 10 units in size and fits unto the screen with a camera depth value of 0.5 (50 units away for a camera whose zFar is 100 and zNear at 1). But yet it is fully visible on the screen when the unprojection calculation says that the screen is 3.2 units wide at 0.5.

I know it is a simple problem but I've been looking at this for a few days and cannot figure it out.

1

1 Answers

0
votes

Well, it turns out that my original premise was flawed.

Camera depth (measured between the far plane Z=1 to the near plane Z=0) is not linearly proportional to the world Z value.

Therefore, I found that I need to first use projectPoint to determine world Z value from camera depth.