要实现3ds max中使用的相机平移效果,必须具备哪些数学条件?
在3ds max中,光标和网格之间的距离在整个移动过程中始终保持不变(mouse_down + mouse_motion + mouse_up)。
我幼稚而失败的尝试一直是试图通过使用dt(帧时间)乘以一些硬编码的常数来将相机移动到XY平面上,结果确实很丑陋和直观。
到目前为止,我得到的代码是:
def glut_mouse(self, button, state, x, y):
self.last_mouse_pos = vec2(x, y)
self.mouse_down_pos = vec2(x, y)
def glut_motion(self, x, y):
pos = vec2(x, y)
move = self.last_mouse_pos - pos
self.last_mouse_pos = pos
self.pan(move)
def pan(self, delta):
forward = vec3.normalize(self.target - self.eye)
right = vec3.normalize(vec3.cross(forward, self.up))
up = vec3.normalize(vec3.cross(forward, right))
if delta.x:
right = right*delta.x
if delta.y:
up = up*delta.y
self.eye+=(right+up)
self.target+=(right+up)
您能解释一下3dsmax中相机平移的数学原理吗?
编辑:
@ Rabbid76最初已经回答了我的问题,但是仍然有一种情况,他的算法无法正常工作。从空的空间开始平移的情况不能正确处理(否则,当深度缓冲区的值取far值= 1.0时表示否则)。在3dsmax中,无论深度缓冲区的值是多少,在所有情况下都可以正确处理摇摄。
最佳答案
在解决方案中,对象的深度是从深度缓冲区中鼠标单击发生的那个位置获取的。如果这是“空空间”,即未绘制任何对象的位置,则深度为深度范围的最大值(通常为1)。这导致快速痛苦。
解决方案或解决方法是使用场景代表位置的深度。例如世界的起源:
pt_drag = glm.vec3(0, 0, 0)
当然,这可能不会在每种情况下都导致正确的结果。如果场景的对象不在世界的起源周围,则此方法将失败。我建议计算场景的axis aligned bounding box的中心。使用此点代表“深度”:box_min = ... # glm.vec3
box_max = ... # glm.vec3
pt_drag = (box_min + box_max) / 2
一个点的深度可以通过使用 View 和投影矩阵以及最终的透视除法进行转换来计算:o_clip = self.proj * self.view * glm.vec4(pt_drag, 1)
o_ndc = glm.vec3(o_clip) / o_clip.w
这可以应用于函数glut_mouse
:def glut_mouse(self, button, state, x, y):
self.drag = state == GLUT_DOWN
self.last_mouse_pos = glm.vec2(x, self.height-y)
self.mouse_down_pos = glm.vec2(x, self.height-y)
if self.drag:
depth_buffer = glReadPixels(x, self.height-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
self.last_depth = depth_buffer[0][0]
if self.last_depth == 1:
pt_drag = glm.vec3(0, 0, 0)
o_clip = self.proj * self.view * glm.vec4(pt_drag, 1)
o_ndc = glm.vec3(o_clip) / o_clip.w
if o_ndc.z > -1 and o_ndc.z < 1:
self.last_depth = o_ndc.z * 0.5 + 0.5
预习:解决问题的方法的关键是找到“正确的”深度。在透视投影时,拖动操作(鼠标移动以1:1 Action 影响对象)投影在视口(viewport)上,并且只能在定义良好的深度下正常工作。当深度不同的对象投影到视口(viewport)时,它们会以不同的比例移动,这就是透视的“自然”。
要找到“正确的”深度,有不同的可能性,这取决于您的需求:
depth_buffer = glReadPixels(x, self.height-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
self.last_depth = depth_buffer[0][0]
d_buf = glReadPixels(0, 0, self.width, self.height, GL_DEPTH_COMPONENT, GL_FLOAT)
d_vals = [float(d_buf[i][j]) for i in range(self.width) for j in range(self.height) if d_buf[i][j] != 1]
if len(d_vals) > 0:
self.last_depth = (min(d_vals) + max(d_vals)) / 2
pt_drag = glm.vec3(0, 0, 0)
o_clip = self.proj * self.view * glm.vec4(pt_drag, 1)
o_ndc = glm.vec3(o_clip) / o_clip.w
if o_ndc.z > -1 and o_ndc.z < 1:
self.last_depth = o_ndc.z * 0.5 + 0.5