旋转相机使用 lookat 功能与平移手势
rotate camera use lookat function with pan gesture
我尝试使用 lookat 功能通过平移手势旋转相机。我使用 swift 和 Metal(在这种情况下,Metal 与 OpenGLES 的工作方式相同)。这是我的代码
观察函数:
let kEye = V3f(0.0, 0.0, -2.0)
var ktarget = V3f(0.0, 0.0, 0.0)
let kUp = V3f(0.0, 1.0, 0.0)
var viewMatrix = lookAt(kEye, center: ktarget, up: kUp)
平移手势:
func pan(panGesture: UIPanGestureRecognizer){
if panGesture.state == UIGestureRecognizerState.Changed{
let pointInView = panGesture.locationInView(self.view)
let xDelta = (lastPanLocation.x - pointInView.x)/self.view.bounds.width * panSensivity
let yDelta = (lastPanLocation.y - pointInView.y)/self.view.bounds.height * panSensivity
lastPanLocation = pointInView
var viewDirection = rotationM3f(kUp, angle: Float(-xDelta)) * viewDirection
var toRotateAround = Cross(viewDirection, b: kUp)
viewDirection = rotationM3f(toRotateAround, angle: Float(-yDelta)) * viewDirection
ktarget = kEye + viewDirection
} else if panGesture.state == UIGestureRecognizerState.Began {
lastPanLocation = panGesture.locationInView(self.view)
}
}
一开始没问题,平移相机一段时间后,viewDirection和toRotateAround向量会变成-0.0,-0.0,0.0,当手指垂直移动但相机不上下看时,任何人知道代码有什么问题吗?谢谢~~~
你只修改了视图方向(最后ktarget
)而忘记了kUP
。一旦这 2 个向量变得平行,叉积为零并且一切都中断了。
您正在寻找的解决方案是使用 viewDirection
和 toRotateAround
的叉积重新计算 kUp
向量。
使用这样的旋转时,您需要将数据视为基本向量和位置 (location=kEye
、forward=ktarget-keye
、up=kUp
、right=cross(forward, up)
)。基向量总是相互垂直(我建议它们也被归一化)并且当你旋转时你总是围绕另一个基向量旋转这些向量之一并且在旋转之后你需要使用叉积重新计算第三个向量.
因此,要向左或向右旋转,您需要将 forward
围绕 up
旋转,然后使用 forward
和 up
之间的叉积得到 right
向量。 (这里的右向量是可选的,因为你不使用它)
要向上或向下旋转,您可以围绕 right
向量旋转 forward
向量,并使用 forward
和 right
之间的叉积来获得新的 top
向量。
然后为了向左或向右倾斜,您围绕 forward
旋转 up
并得到 right
向量与两个使用的向量的叉积。
如果你看到逻辑你会发现总是有两种方式沿着一个轴旋转。例如,要向左或向右旋转,您还可以围绕 up
旋转 right
向量,并使用 right
和 up
的叉积找到新的前向向量。
虽然有一个技巧。此处描述的过程非常适合自由运动,例如您可以 "tilt" 的飞行模拟。它不适合移动,例如第一人称射击游戏,向上始终水平位于屏幕中央(我希望您能看到其中的区别)。要创建这种 FPS 方式,您实际上确实需要将 up
保持为 (0,1,0)
,但向前绝不能是 (0,1,0)
,但可以是 (0.001, 0.09, 0)
,这非常接近直接向上看.所以只要你将向上的角度限制在某个值就应该没问题。还有其他方法...
我尝试使用 lookat 功能通过平移手势旋转相机。我使用 swift 和 Metal(在这种情况下,Metal 与 OpenGLES 的工作方式相同)。这是我的代码
观察函数:
let kEye = V3f(0.0, 0.0, -2.0)
var ktarget = V3f(0.0, 0.0, 0.0)
let kUp = V3f(0.0, 1.0, 0.0)
var viewMatrix = lookAt(kEye, center: ktarget, up: kUp)
平移手势:
func pan(panGesture: UIPanGestureRecognizer){
if panGesture.state == UIGestureRecognizerState.Changed{
let pointInView = panGesture.locationInView(self.view)
let xDelta = (lastPanLocation.x - pointInView.x)/self.view.bounds.width * panSensivity
let yDelta = (lastPanLocation.y - pointInView.y)/self.view.bounds.height * panSensivity
lastPanLocation = pointInView
var viewDirection = rotationM3f(kUp, angle: Float(-xDelta)) * viewDirection
var toRotateAround = Cross(viewDirection, b: kUp)
viewDirection = rotationM3f(toRotateAround, angle: Float(-yDelta)) * viewDirection
ktarget = kEye + viewDirection
} else if panGesture.state == UIGestureRecognizerState.Began {
lastPanLocation = panGesture.locationInView(self.view)
}
}
一开始没问题,平移相机一段时间后,viewDirection和toRotateAround向量会变成-0.0,-0.0,0.0,当手指垂直移动但相机不上下看时,任何人知道代码有什么问题吗?谢谢~~~
你只修改了视图方向(最后ktarget
)而忘记了kUP
。一旦这 2 个向量变得平行,叉积为零并且一切都中断了。
您正在寻找的解决方案是使用 viewDirection
和 toRotateAround
的叉积重新计算 kUp
向量。
使用这样的旋转时,您需要将数据视为基本向量和位置 (location=kEye
、forward=ktarget-keye
、up=kUp
、right=cross(forward, up)
)。基向量总是相互垂直(我建议它们也被归一化)并且当你旋转时你总是围绕另一个基向量旋转这些向量之一并且在旋转之后你需要使用叉积重新计算第三个向量.
因此,要向左或向右旋转,您需要将 forward
围绕 up
旋转,然后使用 forward
和 up
之间的叉积得到 right
向量。 (这里的右向量是可选的,因为你不使用它)
要向上或向下旋转,您可以围绕 right
向量旋转 forward
向量,并使用 forward
和 right
之间的叉积来获得新的 top
向量。
然后为了向左或向右倾斜,您围绕 forward
旋转 up
并得到 right
向量与两个使用的向量的叉积。
如果你看到逻辑你会发现总是有两种方式沿着一个轴旋转。例如,要向左或向右旋转,您还可以围绕 up
旋转 right
向量,并使用 right
和 up
的叉积找到新的前向向量。
虽然有一个技巧。此处描述的过程非常适合自由运动,例如您可以 "tilt" 的飞行模拟。它不适合移动,例如第一人称射击游戏,向上始终水平位于屏幕中央(我希望您能看到其中的区别)。要创建这种 FPS 方式,您实际上确实需要将 up
保持为 (0,1,0)
,但向前绝不能是 (0,1,0)
,但可以是 (0.001, 0.09, 0)
,这非常接近直接向上看.所以只要你将向上的角度限制在某个值就应该没问题。还有其他方法...