本文介绍了如何在3dsMax中实现相机平移?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要实现3ds max中使用的摄像机平移效果,必须具备哪些数学运算法则?

What are the necessary maths to achieve the camera panning effect that's used in 3ds max?

在3ds max中,光标和网格之间的距离在整个移动过程中始终保持相同(mouse_down + mouse_motion + mouse_up).

In 3ds max the distance between the cursor and the mesh will always remain the same throughout the entire movement (mouse_down+mouse_motion+mouse_up).

我幼稚而失败的尝试一直是尝试通过使用dt(帧时间)乘以一些硬编码常量来将相机移动到XY平面上,结果确实很丑陋而且很直观.

My naive and failed attempt has been trying to move the camera on the plane XY by using dt (frame time) multiplied by some hardcoded constant and the result is really ugly and uintuitive.

到目前为止,我得到的代码是:

The code I've got so far is:

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中摄像机平移的数学原理吗?

Could you explain how the maths of camera panning in 3dsmax work?

@ Rabbid76最初已经回答了我的问题,但是仍然有一种情况,他的算法无法正常工作.从空的空间开始平移的情况不能正确处理(否则,当深度缓冲区的值取far值= 1.0时表示否则).在3dsmax中,无论深度缓冲区的值是什么,在任何情况下都可以正确处理摄影机的摇摄.

My question has already been answered initially by @Rabbid76 but there's still one case where his algorithm won't work properly. It doesn't handle properly the case where you panning is started from empty space (said otherwise, when depth buffer value takes the far value=1.0). In 3dsmax camera panning is handled correctly in all situations, no matter which value of the depth buffer.

推荐答案

在解决方案中,对象的深度从发生鼠标单击的那个位置的深度缓冲区中获取.如果这是空空间",即未绘制任何对象的位置,则深度为深度范围的最大值(通常为1).这导致了快速的痛苦.

In the solution the depth of the object is taken from the depth buffer, at that position, where the mouse click occurs. If this is the "empty space", a position where no object was drawn, the the depth is the maximum of the depth range (in common 1). This leads to a rapid paining.

一种解决方案或解决方法是使用场景代表位置的深度.例如世界的起源:

A solution or workaround would be use the depth of an representative position of the scene. e.g. the origin of the world:

pt_drag = glm.vec3(0, 0, 0)

当然,在每种情况下,这可能不会导致正确的结果.如果场景的对象不在世界的起源周围,则此方法将失败.我建议计算场景的轴对齐边界框的中心.使用此点代表深度":

Of course this may not lead to a proper result in each case. If the objects of the scene are not around the origin of the world, this approach will fail. I recommend to calculate the center of the axis aligned bounding box of the scene. Use this point for the representative "depth":

box_min = ... # glm.vec3
box_max = ... # glm.vec3

pt_drag = (box_min + box_max) / 2

可以通过使用视图和投影矩阵以及最终的透视划分进行转换来计算点的深度:

The depth of a point can computed by the transformation with the view and projection matrix and a final perspective divide:

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动作影响对象)投影在视口上,并且只能在定义良好的深度下正常工作.当深度不同的对象投影到视口时,它们会以不同的比例移动,这就是透视的本质".

The key to a well feeling solution is to find the "correct" depth. At perspective projection the dragging, where the mouse movement effects the object in a 1:1 motion, projected on the viewport, only works correctly for a well defined depth. Objects with different depths are displaced by a different scale when they projected on the viewport, that's the "nature" of perspective.

要找到正确的"深度,有不同的可能性,这取决于您的需求:

To find the "correct" depth, there are different possibilities, which depend on your needs:

  • 从深度缓冲区读取当前鼠标位置的深度:
depth_buffer = glReadPixels(x, self.height-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
self.last_depth = depth_buffer[0][0]

  • 获取深度缓冲区的最小和最大深度(远平面的值除外, 1.0 ),然后计算平均深度.当然,在这种情况下,必须对整个深度缓冲区进行调查:
    • Get the minimum and maximum depth of the depth buffer (except the value for the far plane, 1.0) and calculate the mean depth. Of course the entire depth buffer has to be investigated in this case:
    • 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
        

        • 计算场景边界框的中心.

          • Calculating the center of the bounding box of the scene.

            实施射线投射,该射线投射通过射线识别对象,该射线从视点开始穿过光标(鼠标)位置.当没有物体被击中时,可以通过识别最接近射线的物体来改进该算法.

            Implement a raycasting, which identifies an object by a ray, which starts at the point of view a runs trough the cursor (mouse) position. This algorithm can be advanced by identifying the object which is "closest" to the ray, when no object is hit.

            这篇关于如何在3dsMax中实现相机平移?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 12:56