如果你看到这则信息,这表示下载可汗学院的外部资源时遇到困难.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

主要内容

生成 3D 图形

现在我们已经有了一个立方体,但如果我们想改变它的位置或大小呢?或者我们想要一个长方体或许多长方体呢?根据我们现在的代码,我们必须要逐一改变每一个点,非常麻烦。我们所想要的是用一个简单的方法创建具有特定尺寸和位置的长方体。换句话说,我们需要一个将位置和尺寸映射到一个数组的点和一个数组的边之中的函数。

定义长方体

一个长方体有三个尺寸:长度、宽度和高度:
它还有一个在三维空间中的坐标,这样就是六个参数。有好几种方法可以定义立方体的位置:我们可以使用其中心或者一个角。前者比较常见,但我认为后者更容易使用。
我们的函数需要一起返回 nodes(点)数组和 edge(边)数组。返回两个变量的一种方法是将变量打包到一个包含 nodes 值和 edges 值的对象中。注意,你可以使用任何名字来称呼这些变量,我只是觉得使用同一个命名更便于理解。
// 创建一个立方体,其其中一个顶点在位置 (x, y, z) 
// 长度为 d,宽度为 w,高度为 h。
var createCuboid = function(x, y, z, w, h, d) {
   var nodes = [];
   var edges = [];
   var shape = { 'nodes': nodes, 'edges': edges };
   return shape;
};
如果我们使用该函数来创建一个长方体,我们就需要用这种方式获得第一个点:
var shape = createCuboid(0, 0, 0, 100, 160, 50);
var node0 = shape.nodes[0];
这段代码将会将 node0 赋值为 nodes 数组的第一个值。但是,目前 nodes 和 edges 数组中没有值。
我们将点定义为坐标的组合,不管它具有或不具有与之对应的具体值。边的定义方式与之前相同(只是我现在一次性定义所有边,而不是单独定义每一个边)。注意这个函数允许你将长方体参数定义为负值。
var createCuboid = function(x, y, z, w, h, d) {
   var nodes = [[x, y, z ], [x, y, z+d], [x, y+h, z ], [x, y+h, z+d], [x+w, y, z ], [x+w, y, z+d], [x+w, y+h, z ], [x+w, y+h, z+d]];

   var edges = [[0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7]];

   return { 'nodes': nodes, 'edges': edges};
};
然后我们可以创建一个长度50,宽度100,高度160,而且其中一个顶点在原点的立方体,如下所示:
var shape = createCuboid(0, 0, 0, 100, 160, 50);
由于我们以前的代码只引用了全局nodesedges 变量, 因此我们需要将对象的属性存储到这些全局变量中:
var nodes = shape.nodes; var edges = shape.edges;
您可以在下面看到完整的代码。

使用多个形状

我们可以创建具有不同尺寸的图形,如果我们想要不止一个图形怎么办?每当我们需要多个物体的时候,使用数组是最高效的。让我们来创建一个包含图形的数组。
var shape1 = createCuboid(-120, -20, -20, 240, 40, 40);
var shape2 = createCuboid(-120, -50, -30, -20, 100, 60);
var shape3 = createCuboid( 120, -50, -30, 20, 100, 60);
var shapes = [shape1, shape2, shape3];
现在我们需要改变显示和旋转方法来处理包含对象的数组。首先,用一个 for 循环遍历每个图形来显示它们的边:
// 绘制边
stroke(edgeColor);
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
   var nodes = shapes[shapeNum].nodes;
   var edges = shapes[shapeNum].edges;
   for (var e = 0; e < edges.length; e++) {
      var n0 = edges[e][0];
      var n1 = edges[e][1];
      var node0 = nodes[n0];
      var node1 = nodes[n1];
      line(node0[0], node0[1], node1[0], node1[1]);
   }
}
然后用一个类似的 for 循环来显示点:
// 绘制点
fill(nodeColor);
noStroke();
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
   var nodes = shapes[shapeNum].nodes;
   for (var n = 0; n < nodes.length; n++) {
      var node = nodes[n]; ellipse(node[0], node[1], nodeSize, nodeSize);
   }
}
我们可以在每个旋转方法中加入一个类似的 for 循环,但我认为将包含点的数列直接传入每一个函数的方法更加灵活 - 这样我们就可以独立的旋转每一个图形。举个例子,rotateZ3D() 函数就会像这样:
var rotateZ3D = function(theta, nodes) { ... };
现在当我们使用鼠标来旋转的时候,我们必须遍历每一个图形并且为每一个图形调用旋转函数:
mouseDragged = function() {
   var dx = mouseX - pmouseX;
   var dy = mouseY - pmouseY;
   for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
      var nodes = shapes[shapeNum].nodes;
      rotateY3D(dx, nodes);
      rotateX3D(dy, nodes);
   }
};
确保你移除了其他不传入特定点的旋转函数的调用。完整的代码如下所示:

想加入讨论吗?

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