【问题标题】:Which face is further in parallel projections?哪个面在平行投影中更远?
【发布时间】:2026-01-14 05:25:01
【问题描述】:

我正在一个 几何程序 上使用 JavaScript(React) 创建指定 对象轴测(更好的平行投影) 由顶点和面定义(一个面可以有不同数量的顶点)。

当您不需要不透明的面时,它可以完美运行,否则其他面应该在下面。

所以我想从远到近排序我的面孔列表:

[
[[100, 0, 100], [50, 50, 50], [120, 170, 120], [10, 200, 150]],
[[10, 20, 30], [10, 200, 250], [50, 50, 50], [100, 30, 30]],...
]

我将使用faces.sort(sortingFunction)

我不关心相交的面孔

(它将所有对象的面放在一起)

sortingFunction应该怎么做?

您必须考虑轴测图是如何定义的。它由X-、Y-轴旋转定义(Xrotation 可以大于或小于Yrotation),Z 旋转为π/4(90°)

这是一个旧版本的应用程序,让您明白我的意思:http://dev_proiezioni.surge.sh/

对不起我糟糕的英语。 谢谢

【问题讨论】:

    标签: javascript reactjs math geometry


    【解决方案1】:

    您尝试执行的操作称为“背面剔除”。一种常见的技术是确定多边形表示中的点列表从相机的角度来看是顺时针还是逆时针。这需要您非常小心地创建顶点列表。有关更多详细信息,请查看 wikipedia 文章:https://en.wikipedia.org/wiki/Back-face_culling。实施部分描述了您必须将其转换为 JavaScript 所涉及的数学。请注意,此技术比对人脸列表进行排序更快,因为它只需要检查每个人脸一次,而不是将每个人脸与其他人脸进行比较。

    【讨论】:

      【解决方案2】:

      我不关心相交的面孔

      这意味着我们可以通过取中间点来将平原简化为点:

      const vecOp = op => (a, b) => a.map((c, i) => op(c,  b[i] || b));
      
      const add = vecOp((a, b) => a + b);
      const sub = vecOp((a, b) => a - b);
      const mul = vecOp((a, b) => a * b);
      const div = vecOp((a, b) => a / b);
       const sum = v => v.reduce((a, b) => a + b, 0);
      
      const middle = (a, b) =>  div(add(a, b), 2);
      
       const planeToPoint = ([a, b, c, d]) => middle(
         middle(a, b),
         middle(c, d)
       );
      

      现在按“靠近相机”排序,可以在两个平面的中心之间画一条线,这将产生一个方向:

       const aToB = (a, b) => 
         sub(
          planeToPoint(b),
          planeToPoint(a)
        );
      

      现在我们可以turn the camera rotation into a camera lookAt vector :

       const rotToVec = (yaw, pitch) => ([
         Math.cos(yaw) * Math.cos(pitch),
         Math.sin(yaw) * Math.cos(pitch),
         Math.sin(pitch)
       ]);
      

      并且可以将该方向与相机方向进行比较,从而得出它们之间的角度:

       const angle = (a, b) => sum(mul(a, b)) / sum(a) * sum(b)
      

      现在让我们一起来看看:

       const camVec = rotToVec(cam.yaw, cam.pitch);
      
       planes.sort((a, b) =>
        Math.abs(angle(aToB(a, b), camVec)) < Math.PI / 4 /*90°*/ ? 1 : -1
       );
      

      免责声明:我没有尝试过上面的代码,也没有使用过平行投影,我也不擅长数学,所以请谨慎使用,我不知道我在说什么

      【讨论】:

      • 非常感谢,但您是否注意到我的投影是由轴的 2d 旋转定义的,所以我如何获得 cam.jaw 和 cam.pitch?
      • @strategaD 是偏航和俯仰
      【解决方案3】:

      对于近似解,使用 3D 到 3D 变换,并考虑 Z 坐标。对于每个面,保留最近的 Z 并在此 Z 上对面进行排序。

      如需更精确的解决方案,请考虑https://en.wikipedia.org/wiki/Newell%27s_algorithm

      【讨论】:

        【解决方案4】:

        按与您的摄像头的距离排序。

        function distanceBetweenTwoPoints (p1, p2) {
          return Math.hypot(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z)
        }
        
        function sortFunction (p1, p2) {
          return distanceBetweenTwoPoints(camera, p1) > distanceBetweenTwoPoints(camera,p2) ? -1 : 1
        }
        

        根据您想要的顺序调整符号 &gt;

        【讨论】:

        • 谢谢,但是如何在平行投影中找到相机位置?我应该如何将一张脸近似为一个点?中位数?
        • 啊,确实。没有考虑清楚,抱歉。我认为您会在一些与数学相关的 Stack Exchange 网站上获得更多答案。