作为对这个问题的直接回答:投影矩阵旨在输出 -1 ... 1 范围内的内容,而 CoreAnimation 通常适用于像素。烦恼由此而来。所以要解决这个问题,你应该在将模型视图乘以投影矩阵之前,减少你的模型以很好地适应 -1 ... 1 范围(取决于你的世界是你可以将它划分为 bounds.size)和投影矩阵乘法之后你可以返回像素。
所以下面是一段Swift中的代码片段(我个人很讨厌Swift,但是我的客户很喜欢)相信可以看懂。它是为 CoreAnimation 和 ARKit 编写的,并测试过它可以工作。我希望 ARKit 矩阵可以被认为是 opengl 矩阵
func initCATransform3D(_ t_ : float4x4) -> CATransform3D
{
let t = t_.transpose //surprise m43 means 4th column 3rd row, thanks to Apple for amazing documenation for CATransform3D and total disregarding standard math with Mij notation
//surprise: at Apple didn't care about CA & ARKit compatibility so no any easier way to do this
return CATransform3D(
m11: CGFloat(t[0][0]), m12: CGFloat(t[1][0]), m13: CGFloat(t[2][0]), m14: CGFloat(t[3][0]),
m21: CGFloat(t[0][1]), m22: CGFloat(t[1][1]), m23: CGFloat(t[2][1]), m24: CGFloat(t[3][1]),
m31: CGFloat(t[0][2]), m32: CGFloat(t[1][2]), m33: CGFloat(t[2][2]), m34: CGFloat(t[3][2]),
m41: CGFloat(t[0][3]), m42: CGFloat(t[1][3]), m43: CGFloat(t[2][3]), m44: CGFloat(t[3][3])
)
}
override func updateAnchors(frame: ARFrame) {
for animView in stickerViews {
guard let anchor = animView.anchor else { continue }
//100 here to make object smaller... on input it's in pixels, but we want it to be more real.
//we work in meters at this place
//surprise: nevertheless the matrices are column-major they are inited in "transposed" form because arrays/columns are written in horizontal direction
let mx = float4x4(
[1/Float(100), 0, 0, 0],
[0, -1/Float(100), 0, 0], //flip Y axis; it's directed up in 3d world while for CA on iOS it's directed down
[0, 0, 1, 0],
[0, 0, 0, 1]
)
let simd_atr = anchor.transform * mx //* matrix_scale(1/Float(bounds.size.height), matrix_identity_float4x4)
var atr = initCATransform3D(simd_atr) //atr = anchor transform
let camera = frame.camera
let view = initCATransform3D(camera.viewMatrix(for: .landscapeRight))
let proj = initCATransform3D(camera.projectionMatrix(for: .landscapeRight, viewportSize: camera.imageResolution, zNear: 0.01, zFar: 1000))
//surprise: CATransform3DConcat(a,b) is equal to mathematical b*a, documentation in apple is wrong
//for fun it's distinct from glsl or opengl multiplication order
atr = CATransform3DConcat(CATransform3DConcat(atr, view), proj)
let norm = CATransform3DMakeScale(0.5, -0.5, 1) //on iOS Y axis is directed down, but we flipped it formerly, so return back!
let shift = CATransform3DMakeTranslation(1, -1, 0) //we should shift it to another end of projection matrix output before flipping
let screen_scale = CATransform3DMakeScale(bounds.size.width, bounds.size.height, 1)
atr = CATransform3DConcat(CATransform3DConcat(atr, shift), norm)
atr = CATransform3DConcat(atr, CATransform3DMakeAffineTransform(frame.displayTransform(for: self.orientation(), viewportSize: bounds.size)));
atr = CATransform3DConcat(atr, screen_scale) //scale back to pixels
//printCATransform(atr)
//we assume center is in 0,0 i.e. formerly there was animView.layer.center = CGPoint(x:0, y:0)
animView.layer.transform = atr
}
}
PS 我认为对于使用左右坐标系、Y 和 Z 轴方向、列主要矩阵、浮点和 CGFloat 不兼容以及缺乏 CA 和 ARKit 集成创建所有可能混合的开发人员来说,地狱中的一个地方将特别热门。 ..