如何在 3dsMax 中实现相机平移? [英] How to implement camera pan like in 3dsMax?

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

问题描述

实现 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)移动 = self.last_mouse_pos - posself.last_mouse_pos = posself.pan(移动)def pan(self, delta):forward = vec3.normalize(self.target - self.eye)右 = vec3.normalize(vec3.cross(forward, self.up))up = vec3.normalize(vec3.cross(forward, right))如果 delta.x:右 = 右 * delta.x如果 delta.y:向上 = 向上 * delta.yself.eye+=(右+上)self.target+=(右+上)

您能解释一下 3dsmax 中相机平移的数学原理吗?

@Rabbid76 最初已经回答了我的问题,但仍有一种情况他的算法无法正常工作.它不能正确处理从空白空间开始平移的情况(否则,当深度缓冲区值采用远值 = 1.0 时).在 3dsmax 中,无论深度缓冲区的哪个值,在所有情况下都能正确处理相机平移.

解决方案

[...] 但仍有一种情况他的算法无法正常工作.它不能正确处理从空白处开始平移的情况 [...]

在解决方案中,对象的深度取自深度缓冲区,在鼠标点击发生的那个位置.如果这是空白空间",即没有绘制对象的位置,则深度是深度范围的最大值(通常为 1).这会导致快速疼痛.

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

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

当然,这可能不会在每种情况下都产生正确的结果.如果场景的对象不在世界原点附近,这种方法就会失败.我建议计算场景的

感觉良好的解决方案的关键是找到正确"的解决方案.深度.在透视投影中,鼠标移动以 1:1 运动影响对象的拖动,投影在视口上,仅适用于明确定义的深度.具有不同深度的物体在投影到视口时会以不同的比例发生位移,这就是自然".视角.

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

  • 从当前鼠标位置的深度缓冲区读取深度:

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]如果 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如果 o_ndc.z >-1 和 o_ndc.z <1:self.last_depth = o_ndc.z * 0.5 + 0.5

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

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

另见 Python OpenGL4.6、GLM导航

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

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).

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)

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

EDIT:

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.

解决方案

[...] 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 [...]

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 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

This can be applied to the function 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

Preview:

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:

  • Reading the depth from the depth buffer at the current mouse position:

depth_buffer = glReadPixels(x, self.height-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)    
self.last_depth = depth_buffer[0][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 

  • Use the origin of the world:

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.

See also Python OpenGL 4.6, GLM navigation

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆