将鼠标坐标转换为3D平面

6
我正在使用JavaScript/jQuery(基于DOM而非canvas)构建一种拖放应用程序。
这个想法是能够在3D场景中拖动div(一个在3D中旋转的div)。
它在2D平面上工作正常,问题是当我将场景旋转到3D时,对象的位置不反映实际鼠标位置,而是在3D中翻译的坐标。
举个例子: exemple JSFIDDLE上的例子 我希望对象相对于鼠标的绝对位置移动。
我像这样计算鼠标位置:
document.addEventListener(gestureMove, function (event) {
  if (mouseDown == true) {
  event.preventDefault();
  moveX = (event.pageX - $('#scene').offset().left);
  moveY = (event.pageY - $('#scene').offset().top);
}

#scene { 
  width: 1000px;
  height: 1000px;
  -webkit-transform-style: preserve-3d;
  -webkit-transform: rotateX( 35deg ); 
}

一个早期的解决方案是根据初始位置计算鼠标位置和对象之间的差异,并在拖动期间将其添加到对象位置上。这种方法可以工作,但动画非常生硬,一点也不流畅。
我确信有更简单的方法来获取相对于3D平面的鼠标坐标,但目前还没有找到真正的解决方案。
关于这个问题的大部分搜索结果指向游戏相关的语言或者canvas/webgl方面的问题。
有什么想法吗?
谢谢。

我认为Google Sketch-up做得很好。 - Diodeus - James MacFarlane
你可能最终会做透视投影的反向操作:http://en.wikipedia.org/wiki/3D_projection#Perspective_projection - Blender
你能将你的代码精简到足够小,以适应 jsfiddle 吗? - Shmiddty
你不需要考虑Z轴吗?还有X和Y轴? - classicjonesynz
@Shmiddty 我更新了问题 (http://jsfiddle.net/JulianG/Aubcr/)。 - Julian
https://dev59.com/ZLPma4cB1Zd3GeqPmzfB - gman
1个回答

2
假设您的鼠标位置是绝对屏幕位置,并且您想要根据鼠标位置直接在3D平面上抓取和滑动对象:
您可以将3D目标平面表示为:
- 3D原点O - 两个3D向量U和V,代表U轴和V轴的方向
因此,给定一个对应于平面坐标[u,v]的3D点是:
point3d P = O + u*U + v*V

接下来,您可以将将映射特定3D点到屏幕的操作组合起来; 这通常用3D变换矩阵ModelMatrixViewMatrixProjectionMatrix描述,并由2D屏幕原点origin_2d和2D缩放向量scale_2d确定视口变换。为了以一种易于反演的方式解决问题,通过为每个元素添加.w坐标,将所有元素提升为齐次坐标。这个额外的坐标作为缩放因子--要获取笛卡尔坐标,您需要将齐次.x.y值除以.w值:

P_hom = [u, v, 1] * [U.x, U.y, U.z, 0] = [u, v, 1] * TexMatrix
                    [V.x, V.y, V.z, 0]
                    [O.x, O.y, O.z, 1]

P_clip_hom = P_hom * ModelMatrix * ViewMatrix * ProjectionMatrix
           = P_hom * ModelViewProjectionMatrix

screenpos_hom = P_clip_hom * [scale_2d.x     0        0] = P_clip_hom * PortMatrix
                             [   0        scale_2d.y  0]
                             [   0           0        0]
                             [origin_2d.x origin_2d.y 1]

So, screenpos_hom = [u, v, 1] * TexMatrix * ModelViewProjectionMatrix * PortMatrix
                  = [u, v, 1] * TexToScreenMatrix

-> [screenpos.x, screenpos.y] = [screenpos_hom.x, screenpos_hom.y] / screenpos_hom.w

请注意,TexToScreenMatrix 是一个3x3的矩阵;您应该能够对其进行反转:
UV_2d_hom = [screenpos.x, screenpos.y, 1] * (TexToScreenMatrix)^-1

-> [u, v] = [UV_2d_hom.x, UV_2d_hom.y] / UV_2d_hom.w

最后,您可以直接使用[u,v]坐标,或者像上面描述的那样使用它们重新创建3D点P。

很抱歉,我不知道这些在javascript/webkit平台上是如何绑定的。如果您无法直接访问webkit变换矩阵,则可能需要在javascript中匹配3D数学 - 例如,如果您使用rotateX(35deg),则不幸的是,您需要设置自己的3D X旋转矩阵为35度,并将其乘入您自己的ViewMatrix... - comingstorm
关于"w",我在我的回答中试图简要解释它;通过"w"进行除法是让你首先能够进行3D透视的原因。如果你想更好地理解它,你需要一个更长、更好的基础3D数学介绍;可以尝试维基百科上的3D投影 - comingstorm
如果维基百科文章和其他通过关键字如“3D”、“透视”、“投影”和“同次”在谷歌上搜索到的链接不好,你最好使用实际的书籍。我推荐Alan Watt的《3D计算机图形学》,但是有各种好的文本可用-看看你本地大学图书馆能找到什么。 - comingstorm
似乎可以获取 Webkit 变换矩阵... 我会深入研究的。 - Julian
@Shmiddty谢谢,结果好了些,但是当你靠近边缘时,即使是垂直拖动也会失败... 我正在尝试从另一个角度解决问题,我找到了一种方法来获取相对于3D场景而不是屏幕的准确鼠标坐标,参考这个答案:https://dev59.com/s1TTa4cB1Zd3GeqPxv4M#6105908。 但是它不是完美的,现在我必须同时管理对象和3D场景鼠标事件。 - Julian
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接