If you're seeing this message, it means we're having trouble loading external resources on our website.

如果你被网页过滤器挡住,请确保域名*.kastatic.org*.kasandbox.org 没有被阻止.

主要内容

旋转 3D 图形

在三维空间内旋转物体听起来很复杂而且有时候确实也是这样的,但是还是有一些很简单的旋转。举个例子,试想将一个立方体绕Z轴(已在屏幕上表示)旋转,我们实际上就是在二维空间内旋转一个正方形:

学习三角函数的理由

三角图
我们可以通过观察一个在(x, 0)的点来继续简化步骤。使用简单的三角函数,我们可以找到该点在绕原点旋转 θ 后的位置:
x=x×cos(θ)y=x×sin(θ)
如果你不知道这些等式是怎么来的,这个视频 可能会有所帮助。

将一点绕原点旋转

三角图
上面的例子告诉了我们怎么旋转一个在x轴上的点,但如果一个点不在x轴上呢?我们需要使用更复杂的三角函数来处理。如果设点 (x, y) 和原点之间的距离为 r,点 (x, y) 和x轴之间的角度为 α,那么:
x=r×cos(α)y=r×sin(α)
如果我们将其旋转β 至点 (x', y'),则:
x=r×cos(α+β)y=r×sin(α+β)
使用一些三角函数恒等式,我们得到:
x=r×cos(α)cos(β)r×sin(α)sin(β)y=r×sin(α)cos(β)+r×cos(α)sin(β)
代入上述 x 值和 y 值,我们得到一个新坐标关于旧坐标和旋转角度的方程:
x=x×cos(β)y×sin(β)y=y×cos(β)+x×sin(β)

写一个旋转的函数

现在我们知道了旋转背后的数学原理,我们可以写一个函数来绕z轴旋转一个点,或者更进一步,一个数组的点。这个函数会遍历数组中的所有点,找到其当前的 x 和 y 坐标,然后更新为新坐标。我们把sin(theta)cos(theta) 写在循环以外,这样我们只需要计算它们一次:
var rotateZ3D = function(theta) {
   var sinTheta = sin(theta);
   var cosTheta = cos(theta);
   for (var n = 0; n < nodes.length; n++) {
      var node = nodes[n];
      var x = node[0];
      var y = node[1];
      node[0] = x * cosTheta - y * sinTheta;
      node[1] = y * cosTheta + x * sinTheta;
   }
};
如果要将一个正方体旋转30度,我们可以这样调用该函数:
rotateZ3D(60);
被旋转后的立方体如下所示 -- 它比之前稍微有意思了一些,但变化不大:

在三个维度上旋转

我们现在可以在两个维度内旋转立方体,但它看起来仍旧像一个正方形。如果我们要围绕 x 轴(水平轴)旋转这个立方体呢?如果想象我们在让它绕 x 轴旋转的时候沿着 y 轴观察这个立方体,我们会看到一个旋转的正方形,就像我们刚才绕 z 轴旋转的时候一样。
我们可以沿用之前的函数和三角函数,只是替换 z 轴为 x 轴。这样,点的 x 坐标不会改变,只用改 y 和 z:
var rotateX3D = function(theta) {
   var sinTheta = sin(theta);
   var cosTheta = cos(theta);
   for (var n = 0; n < nodes.length; n++) {
      var node = nodes[n];
      var y = node[1];
      var z = node[2];
      node[1] = y * cosTheta - z * sinTheta;
      node[2] = z * cosTheta + y * sinTheta;
   }
};
我们可以使用相同的参数来创建让立方体围绕 y 轴旋转的函数:
var rotateY3D = function(theta) {
   var sinTheta = sin(theta);
   var cosTheta = cos(theta);
   for (var n = 0; n < nodes.length; n++) {
      var node = nodes[n];
      var x = node[0];
      var z = node[2];
      node[0] = x * cosTheta + z * sinTheta;
      node[2] = z * cosTheta - x * sinTheta;
   }
};
现在我们已经定义了这些函数,我们可以绕另外两个轴旋转60度:
rotateX3D(60);
rotateY3D(60);
您可以在下面看到完整的代码。尝试拖动滑块来改变调用函数时的参数。

旋转方向

当我们旋转物体时,它可以顺时针旋转或逆时针旋转。 你能告诉我们的立方体转向哪个方向吗? 如果你不确定,滚动到顶部学习按z轴旋转的立方体,然后回到这里。
如果你在数学课里学习了3D旋转,你可能会感到惊奇。正向的旋转通常是逆时针,如下图所示:
三轴的图表:x, y, 和 z, y 朝上。每个轴周围的箭头曲成逆时针弯曲。
这个图表显示什么是右手坐标系,把你的右手绕在轴上就会告诉你旋转方向。
可是, 在我们JS的工作环境中, y-轴是朝下而不是朝上的。
三轴的图表:x, y, 和 z, y 朝上。每个轴周围的箭头曲成逆时针弯曲。
这是一个左手坐标系统,把左手绕在轴上就可以显示旋转方向。 在这种制度中,正向旋转是顺时针的。
许多计算机图形系统使用左手坐标系统,因为[0,0] 点是屏幕的左上角

用户交互

我们可以通过添加函数调用来旋转立方体,但是如果我们可以让使用者使用鼠标来旋转这个立方体的话就会更加实用(也更爽)。为此,我们需要创建一个 mouseDragged() 函数。每当拖动鼠标时,这个函数都会自动被调用。
mouseDragged = function() {
   rotateY3D(mouseX - pmouseX);
   rotateX3D(mouseY - pmouseY);
};
mouseXmouseY 都是包含鼠标当前位置的内建变量。pmouseXpmouseY 是包含上一帧中鼠标位置的内建变量。 因此当 x 坐标增加的时候 (向右移动鼠标),我们会将一个正值传递到 rotateY3D() 中并围绕 y 轴逆时针旋转立方体。
你可以自己试一试。

想加入讨论吗?

尚无帖子。
你会英语吗?单击此处查看更多可汗学院英文版的讨论.