我建议围绕视图空间中的轴进行旋转
您必须知道视图矩阵(
V)。由于视图矩阵被编码在
self.eye,
self.target并且
self.up,它必须由计算
lookAt:
V = glm.lookAt(self.eye, self.target, self.up)
计算
pivot视图空间,旋转角度和旋转轴。在这种情况下,轴是向右旋转的方向,其中y轴必须翻转:
pivot = glm.vec3(V * glm.vec4(target.x, target.y, target.z, 1))axis = glm.vec3(-delta.y, -delta.x, 0)angle = glm.length(delta)
设置旋转矩阵
R并计算围绕枢轴的比率矩阵
RP。最后
V通过旋转矩阵变换视图矩阵()。结果是新的视图矩阵
NV:
R = glm.rotate( glm.mat4(1), angle, axis )RP = glm.translate(glm.mat4(1), pivot) * R * glm.translate(glm.mat4(1), -pivot)NV = RP * V
解码
self.eye,
self.target然后
self.up从新的视图矩阵中进行解码
NV:
C = glm.inverse(NV)targetDist = glm.length(self.target - self.eye)self.eye = glm.vec3(C[3])self.target = self.eye - glm.vec3(C[2]) * targetDist self.up = glm.vec3(C[1])
该方法的完整编码
rotate_around_target_view:
def rotate_around_target_view(self, target, delta): V = glm.lookAt(self.eye, self.target, self.up) pivot = glm.vec3(V * glm.vec4(target.x, target.y, target.z, 1)) axis = glm.vec3(-delta.y, -delta.x, 0) angle = glm.length(delta) R = glm.rotate( glm.mat4(1), angle, axis ) RP = glm.translate(glm.mat4(1), pivot) * R * glm.translate(glm.mat4(1), -pivot) NV = RP * V C = glm.inverse(NV) targetDist = glm.length(self.target - self.eye) self.eye = glm.vec3(C[3]) self.target = self.eye - glm.vec3(C[2]) * targetDist self.up = glm.vec3(C[1])
最终,它可以绕世界原点和眼睛位置甚至任何其他点旋转。
def rotate_around_origin(self, delta): return self.rotate_around_target_view(glm.vec3(0), delta)def rotate_target(self, delta): return self.rotate_around_target_view(self.eye, delta)
或者,可以在模型的世界空间中执行旋转。解决方案非常相似。旋转是在世界空间中完成的,因此不必将轴转换为视图空间,并且将旋转应用于视图矩阵(
NV = V *RP)之前:
def rotate_around_target_world(self, target, delta): V = glm.lookAt(self.eye, self.target, self.up) pivot = target axis = glm.vec3(-delta.y, -delta.x, 0) angle = glm.length(delta) R = glm.rotate( glm.mat4(1), angle, axis ) RP = glm.translate(glm.mat4(1), pivot) * R * glm.translate(glm.mat4(1), -pivot) NV = V * RP C = glm.inverse(NV) targetDist = glm.length(self.target - self.eye) self.eye = glm.vec3(C[3]) self.target = self.eye - glm.vec3(C[2]) * targetDist self.up = glm.vec3(C[1])def rotate_around_origin(self, delta): return self.rotate_around_target_world(glm.vec3(0), delta)
当然,两种解决方案都可以结合使用。通过垂直(上下)拖动,视图可以在其水平轴上旋转。通过水平拖动(左右),模型(世界)可以绕其(上)轴旋转:
def rotate_around_target(self, target, delta): if abs(delta.x) > 0: self.rotate_around_target_world(target, glm.vec3(delta.x, 0.0, 0.0)) if abs(delta.y) > 0: self.rotate_around_target_view(target, glm.vec3(0.0, delta.y, 0.0))
考虑到问题的原始代码,为了实现最小程度的侵入性方法,我将提出以下建议:
操作之后,视图的目标应该是
target
函数的输入参数rotate_around_target
。鼠标水平移动应使视图围绕世界的向上矢量旋转
垂直鼠标移动应使视图围绕当前水平轴倾斜
我想出了以下方法:
计算当前视线(
los
),向上矢量(up
)和水平轴(right
)通过将向上矢量投影到由原始向上矢量和当前视线给定的平面上,将向上矢量垂直放置。这是通过Gram–Schmidt正交化得到的。
绕当前水平轴倾斜。这意味着
los
并up
绕right
轴旋转。围绕上向量旋转。
los
并right
旋转up
。计算设置向上并计算眼睛和目标位置,其中目标由输入参数target设置:
def rotate_around_target(self, target, delta):
# get directionslos = self.target - self.eyelosLen = glm.length(los)right = glm.normalize(glm.cross(los, self.up))up = glm.cross(right, los)# upright up vector (Gram–Schmidt orthogonalization)fix_right = glm.normalize(glm.cross(los, self.original_up))UPdotX = glm.dot(fix_right, up)up = glm.normalize(up - UPdotX * fix_right)right = glm.normalize(glm.cross(los, up))los = glm.cross(up, right)# tilt around horizontal axisRHor = glm.rotate(glm.mat4(1), delta.y, right)up = glm.vec3(RHor * glm.vec4(up, 0.0))los = glm.vec3(RHor * glm.vec4(los, 0.0))# rotate around up vectorRUp = glm.rotate(glm.mat4(1), delta.x, up)right = glm.vec3(RUp * glm.vec4(right, 0.0))los = glm.vec3(RUp * glm.vec4(los, 0.0))# set eye, target and upself.eye = target - los * losLen self.target = targetself.up = up



