要实现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
预习:
python - 如何在3dsMax中实现相机平移?-LMLPHP
解决问题的方法的关键是找到“正确的”深度。在透视投影时,拖动操作(鼠标移动以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]
    
  • 获取深度缓冲区的最小和最大深度(远平面的值(1.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
    
  • 计算场景边界框的中心。
  • 实现射线转换,该射线转换通过射线识别对象,该射线从视点开始经过游标(鼠标)位置。当没有物体被击中时,可以通过识别最接近射线的物体来改进该算法。
  • 09-07 04:11