如何计算二十面体的法线

How to calculate normals for an icosahedron?

本文关键字:十面体 何计算 计算      更新时间:2023-09-26

我想计算原点为{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;

但不知怎么的,它并没有正常工作!

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

我用前面提到的立方体函数而不是构造二十面体的立方体函数运行了同样的程序,它运行得非常好,所以我认为误差必须位于这个新函数中才能计算法线。

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

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

附言:请原谅我英语不好。

编辑2:问题解决了!

我改变了三角形数组中向量的顺序,现在所有三角形的行为都像它们应该的那样!

我在这篇文章中也更新了三角形阵列,所以进一步剪切的现在代表了正确的顺序,AFAIK。

我认为您需要的是"叉积",两个向量的向量积。叉积将始终垂直于由两个向量定义的平面。

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二十面体.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