如何计算二十面体的法线?

How to calculate normals for an icosahedron?

我想计算原点为 {0.0, 0.0, 0.0} 的二十面体的法线,但我不知道该怎么做!

例如,当我之前构建立方体时,获得法线相当简单,因为立方体的每个面都平行于 x、y 或 z 轴以及包含顶点和的数组法线看起来像这样:

var vertices = [
  -1.0, -1.0,  1.0,    1.0, -1.0,  1.0,
   1.0,  1.0,  1.0,   -1.0,  1.0,  1.0,

  -1.0, -1.0, -1.0,   -1.0,  1.0, -1.0,
   1.0,  1.0, -1.0,    1.0, -1.0, -1.0,

  -1.0,  1.0, -1.0,   -1.0,  1.0,  1.0,
   1.0,  1.0,  1.0,    1.0,  1.0, -1.0,

  -1.0, -1.0, -1.0,    1.0, -1.0, -1.0,
   1.0, -1.0,  1.0,   -1.0, -1.0,  1.0,

   1.0, -1.0, -1.0,    1.0,  1.0, -1.0,
   1.0,  1.0,  1.0,    1.0, -1.0,  1.0,

  -1.0, -1.0, -1.0,   -1.0, -1.0,  1.0,
  -1.0,  1.0,  1.0,   -1.0,  1.0, -1.0
];

var normals = [
   0.0,  0.0,  1.0,    0.0,  0.0,  1.0,
   0.0,  0.0,  1.0,    0.0,  0.0,  1.0,

   0.0,  0.0, -1.0,    0.0,  0.0, -1.0,
   0.0,  0.0, -1.0,    0.0,  0.0, -1.0,

   0.0,  1.0,  0.0,    0.0,  1.0,  0.0,
   0.0,  1.0,  0.0,    0.0,  1.0,  0.0,

   0.0, -1.0,  0.0,    0.0, -1.0,  0.0,
   0.0, -1.0,  0.0,    0.0, -1.0,  0.0,

   1.0,  0.0,  0.0,    1.0,  0.0,  0.0,
   1.0,  0.0,  0.0,    1.0,  0.0,  0.0,

  -1.0,  0.0,  0.0,   -1.0,  0.0,  0.0,
  -1.0,  0.0,  0.0,   -1.0,  0.0,  0.0
];

但是现在,在我的二十面体上工作,事情变得有点复杂...

编辑 1:

按照给出的答案的说明,我尝试以这种方式计算法线:

首先我将顶点存储在一个多维数组中:

var r = (1 + Math.sqrt(5)) / 2;

var triangles = [

  [ [-1.0, r, 0.0],   [0.0, 1.0, r],      [1.0, r, 0.0] ],
  [ [1.0, r, 0.0],    [0.0, 1.0, -r],    [-1.0, r, 0.0] ],
  [ [1.0, r, 0.0],    [0.0, 1.0, r],      [r, 0.0, 1.0] ],
  [ [1.0, r, 0.0],    [r, 0.0, -1.0],    [0.0, 1.0, -r] ],
  [ [r, 0.0, -1.0],   [1.0, r, 0.0],      [r, 0.0, 1.0] ],
  [ [-1.0, -r, 0.0],  [1.0, -r, 0.0],    [0.0, -1.0, r] ],
  [ [-1.0, -r, 0.0],  [0.0, -1.0, -r],   [1.0, -r, 0.0] ],
  [ [-1.0, -r, 0.0],  [0.0, -1.0, r],    [-r, 0.0, 1.0] ],
  [ [-1.0, -r, 0.0],  [-r, 0.0, -1.0],  [0.0, -1.0, -r] ],
  [ [-r, 0.0, 1.0],   [-r, 0.0, -1.0],  [-1.0, -r, 0.0] ],
  [ [-1.0, r, 0.0],   [-r, 0.0, 1.0],     [0.0, 1.0, r] ],
  [ [-1.0, r, 0.0],   [0.0, 1.0, -r],   [-r, 0.0, -1.0] ],
  [ [-1.0, r, 0.0],   [-r, 0.0, -1.0],   [-r, 0.0, 1.0] ],
  [ [1.0, -r, 0.0],   [r, 0.0, 1.0],     [0.0, -1.0, r] ],
  [ [1.0, -r, 0.0],   [0.0, -1.0, -r],   [r, 0.0, -1.0] ],
  [ [1.0, -r, 0.0],   [r, 0.0, -1.0],     [r, 0.0, 1.0] ],
  [ [0.0, -1.0, -r],  [-r, 0.0, -1.0],   [0.0, 1.0, -r] ],
  [ [0.0, -1.0, -r],  [0.0, 1.0, -r],    [r, 0.0, -1.0] ],
  [ [0.0, 1.0, r],    [-r, 0.0, 1.0],    [0.0, -1.0, r] ],
  [ [0.0, 1.0, r],    [0.0, -1.0, r],     [r, 0.0, 1.0] ]

];

然后,根据给出的答案,我写了这个函数来计算法线...

var normals = [ ];

triangles.forEach(function (triangle) {
  var v1 = triangle[0],
      v2 = triangle[1],
      v3 = triangle[2];
  var p12 = new Array(3);
  p12[0] = v2[0] - v1[0];
  p12[1] = v2[1] - v1[1];
  p12[2] = v2[2] - v1[2];
  var p23 = new Array(3);
  p23[0] = v3[0] - v2[0];
  p23[1] = v3[1] - v2[1];
  p23[2] = v3[2] - v2[2];
  var cp = new Array(3);
  var x1 = p12[0],
      y1 = p12[1],
      z1 = p12[2];
  var x2 = p23[0],
      y2 = p23[1],
      z2 = p23[2];
  cp[0] = y1 * z2 - z1 * y2;
  cp[1] = z1 * x2 - x1 * z2;
  cp[2] = x1 * y2 - y1 * x2;
  var x = Math.pow(cp[0], 2),
      y = Math.pow(cp[1], 2),
      z = Math.pow(cp[2], 2);
  var len = Math.sqrt(x + y + z);
  var normal = new Array(3);
  normal[0] = cp[0] / len;
  normal[1] = cp[1] / len;
  normal[2] = cp[2] / len;
  for (var i = 0; i < 3; i++) {
    normals.push(normal);
  }
});

...最终解压并使用它们:

var unpackedNormals = [ ];

for (var n in normals) {
  unpackedNormals = unpackedNormals.concat(normals[n]);
}

var vertexNormalData = unpackedNormals;

但不知何故,它无法正常工作!

我可以在屏幕上看到二十面体,但表面三角形的光照似乎完全错误。

我运行用我之前提到的立方体函数而不是构造二十面体的那个程序是同一个程序,而且它都工作得很好,所以我认为错误必须位于这个新函数中以计算法线。

也许有人知道我做错了什么?

如有任何帮助,我将不胜感激!

PS: 请原谅我的英语不好

编辑 2:问题已解决!

我更改了三角形数组中向量的顺序,现在所有三角形的行为都应该如此!

我也更新了这个 post 中的三角形数组,所以进一步向上的片段现在代表正确的顺序,AFAIK。

我想你需要的是"cross-product",两个向量的向量积。叉积将始终垂直于两个向量定义的平面。

http://en.wikipedia.org/wiki/Cross_product

对于二十面体的每个三角形,您都有描述边的向量(例如,如果 v1、v2 和 v3 是描述顶点的向量,则边为 p12 = v2-v1、p23 = v3-v2 和 p31 = v1-v3)。您的法线将是标准化的三个向量中的两个的叉积(除以它的模数),例如

n123 = (p12 x p23) / (|p12 x p23|)

你应该知道的有用的东西:

  • 两个向量的差值

    p12 = v2 - v1 = [x2, y2, z2] - [x1, y1, z1] = [x2-x1, y2-y1, z2-z1]
    
  • 向量的模数(长度):

    |v| = |[x, y, z]| = sqrt(x^2 + y^2 + z^2)
    

希望对您有所帮助。

如果您只对结果感兴趣,下面是我从 Blender 导出的 Wavefront icosahedron.obj 文件,其中包含三角顶点、纹理和法线。

# Blender v2.77 (sub 0) OBJ File: 'icosahedron.blend'
# www.blender.org
mtllib icosahedron.mtl
o Icosphere
v 0.000000 -1.000000 0.000000
v 0.723600 -0.447215 0.525720
v -0.276385 -0.447215 0.850640
v -0.894425 -0.447215 0.000000
v -0.276385 -0.447215 -0.850640
v 0.723600 -0.447215 -0.525720
v 0.276385 0.447215 0.850640
v -0.723600 0.447215 0.525720
v -0.723600 0.447215 -0.525720
v 0.276385 0.447215 -0.850640
v 0.894425 0.447215 0.000000
v 0.000000 1.000000 0.000000
vt 0.6739 0.5441
vt 0.5057 0.4329
vt 0.6731 0.1917
vt 0.5162 0.7848
vt 0.8418 0.4311
vt 0.8321 0.7831
vt 0.3418 0.5689
vt 0.3321 0.2169
vt 1.0162 0.2152
vt 1.0057 0.5671
vt 1.1731 0.8083
vt 0.1731 0.8083
vt 0.1739 0.4559
vt 0.0162 0.2152
vt 0.0057 0.5671
vn 0.1876 -0.7947 0.5774
vn 0.6071 -0.7947 0.0000
vn -0.4911 -0.7947 0.3568
vn -0.4911 -0.7947 -0.3568
vn 0.1876 -0.7947 -0.5774
vn 0.9822 -0.1876 0.0000
vn 0.3035 -0.1876 0.9342
vn -0.7946 -0.1876 0.5774
vn -0.7946 -0.1876 -0.5774
vn 0.3035 -0.1876 -0.9342
vn 0.7946 0.1876 0.5774
vn -0.3035 0.1876 0.9342
vn -0.9822 0.1876 0.0000
vn -0.3035 0.1876 -0.9342
vn 0.7946 0.1876 -0.5774
vn 0.4911 0.7947 0.3568
vn -0.1876 0.7947 0.5774
vn -0.6071 0.7947 0.0000
vn -0.1876 0.7947 -0.5774
vn 0.4911 0.7947 -0.3568
usemtl None
s off
f 1/1/1 2/2/1 3/3/1
f 2/2/2 1/1/2 6/4/2
f 1/1/3 3/3/3 4/5/3
f 1/1/4 4/5/4 5/6/4
f 1/1/5 5/6/5 6/4/5
f 2/2/6 6/4/6 11/7/6
f 3/3/7 2/2/7 7/8/7
f 4/5/8 3/3/8 8/9/8
f 5/6/9 4/5/9 9/10/9
f 6/4/10 5/6/10 10/11/10
f 2/2/11 11/7/11 7/8/11
f 3/3/12 7/8/12 8/9/12
f 4/5/13 8/9/13 9/10/13
f 5/6/14 9/10/14 10/11/14
f 6/4/15 10/12/15 11/7/15
f 7/8/16 11/7/16 12/13/16
f 8/14/17 7/8/17 12/13/17
f 9/15/18 8/14/18 12/13/18
f 10/12/19 9/15/19 12/13/19
f 11/7/20 10/12/20 12/13/20