如何为生成的几何计算表面法线


12

我有一个基于调用代码的输入生成3D形状的类。输入的内容包括长度,深度,弧度等。我的代码完美地生成了几何图形,但是在计算表面法线时遇到了麻烦。点亮时,由于计算出的不正确的表面法线,我的形状具有非常奇怪的着色/纹理。从我所有的研究中,我相信我的数学是正确的,似乎我的技术或方法有问题。

在高层次上,如何以编程方式计算所生成形状的表面法线?我在我的代码上使用iOS上的Swift / SceneKit,但是通用答案很好。

我有两个数组代表我的形状。一个是一组3D点,这些点代表组成形状的顶点。另一个数组是第一个数组的索引列表,这些索引将顶点映射为三角形。我需要获取该数据并生成一个第三数组,该数组是一组有助于形状发光的表面法线。(请参见SCNGeometrySourceSemanticNormalSceneKit`

顶点和索引的列表始终根据类的输入而有所不同,因此我无法预先计算或对表面法线进行硬编码。


需要更多上下文。您是否要计算参数化曲面的解析法线?隐式表面?还是要根据通用三角形网格计算法线?或者是其他东西?
内森·里德

谢谢,我添加了更多细节。要回答您的问题,我需要从通用三角形网格计算法线。虽然很清楚,但网格因输入而异。我的形状是3D箭头,例如,这里是它的2种不同形式(即径向和线性)的屏幕截图。该类根据要求更改网格的宽度,深度,长度,弧度和半径。cl.ly/image/3O0P3X3N3d1d您可以看到我为解决此问题而进行的不当尝试所得到的奇怪照明。
macinjosh

3
简短的版本是:将每个顶点法线计算为所有与之接触的三角形的法线的标准化总和。但是,这将使一切平滑,而这可能不是您想要的形状。稍后,我将尝试扩展为完整答案。
内森·里德

我要的是平稳!
macinjosh

4
在大多数情况下,如果您解析计算顶点位置,那么您也可以解析计算法线。对于参数曲面,法线是两个梯度向量的叉积。计算三角形法线的平均值只是一个近似值,通常会导致视觉质量下降。我会发布一个答案,但是我已经在SO上发布了一个详细的示例(stackoverflow.com/questions/27233820/…),我不确定我们是否要在此处复制内容。
Reto Koradi

Answers:


10

您只是不想要完全平滑的结果。内森·里德(Nathan Reed)评论的方法是:“计算每个顶点以面对法线,对其求和,对总和进行归一化”,通常它有时会失败。但这在这里并不重要,我们可以通过向其添加拒绝子句来使用该方法。

在这种情况下,您只是希望某些零件不会相对于某些其他零件进行平滑处理。您需要选择性的硬边。因此,例如,平坦的顶部和底部与侧面的三角形带是分开的,每个平坦的区域也是如此。

我们追求的形象

图片1:您想要的结果。

实际上,您只想对弯曲区域的顶点求平均,其他所有人都可以使用它们单独从三角形获得的法线。因此,您最好将网格视为9个单独的区域,这些区域无需其他区域即可处理。

显示网格和法线]

图像2:显示网格结构和法线的图像。

当然,您可以通过不包括与主要顶点法线成一定角度之外的法线来自动推断出这一点。伪代码:

For vertex in faceVertex:
    normal = vertex.normal
    For adjVertex in adjacentVertices:
        if anglebetween(vertex.normal, adjVertex.normal )  < treshold:
            normal += adjVertex.normal
    normal = normalize(normal)

那行得通,但是您可以在创建时简单地避免所有这些操作,因为您了解单独的平面的工作方式有所不同。因此,只有弯曲的侧面需要法线方向合并。实际上,您可以直接根据基本数学形状对其进行计算。


10

我主要看到三种计算生成形状的法线的方法。

分析法线

在某些情况下,您具有有关曲面的足够信息以生成法线。例如,球体上任何点的法线对于计算而言都是微不足道的。简而言之,当您知道函数的导数时,您也就知道法线。

如果您的案例足够狭窄,可以使用分析法线,则它们可能会在精度方面提供最佳结果。但是,该技术的伸缩性不太好:如果还需要处理无法使用解析法线的案例,那么保留处理一般案例的技术并完全放弃解析可能会更容易。

顶点法线

两个向量的叉积给出一个垂直于它们所属平面的向量。因此,获得三角形的法线很简单:

vec3 computeNormal(vec3 a, vec3 b, vec3 c)
{
    return normalize(crossProduct(b - a, c - a));
}

此外,在上述示例中,叉积的长度与abc内的面积成比例。因此,可以通过对叉积求和并归一化(最后一步),从而按每个三角形的面积加权,来计算由多个三角形共享的顶点处的平滑法线。

vec3 computeNormal(vertex a)
{
    vec3 sum = vec3(0, 0, 0);
    list<vertex> adjacentVertices = getAdjacentVertices(a);
    for (int i = 1; i < adjacentVertices; ++i)
    {
        vec3 b = adjacentVertices[i - 1];
        vec3 c = adjacentVertices[i];
        sum += crossProduct(b - a, c - a);
    }
    if (norm(sum) == 0)
    {
        // Degenerate case
        return sum;
    }
    return normalize(sum);
}

如果使用四边形,可以使用一个不错的技巧:对于四边形abcd,请使用crossProduct(c - a, d - b),它将很好地处理四边形实际上是三角形的情况。

Iñigoquilez就该主题写了几篇简短的文章:网格的聪明归一化,以及n边多边形的法线和面积

来自偏导数的法线

可以在片段着色器中根据偏导数计算法线。除了这次是在屏幕空间中完成之外,后面的数学是相同的。Angelo Pesce的这篇文章介绍了该技术:没有法线的法线


1
有第四种方式,由艺术家提供的法线;)
joojaa 2015年

@joojaa:我假设您是指法线贴图?否则,我从未听说过手动创作的法线。
Julien Guertault 2015年

1
不,手动编写的法线。有时候,您的美术师比程序员的模型更了解法线应该如何表现。如果计算引擎假设法线来自基础计算,有时会有些问题。但是可以肯定的是,这确实发生了,您可以节省大量的数学建模时间。
joojaa 2015年

1
有时将它们称为“显式法线”(3ds max和maya术语)。
Dusan Bosnjak'pailhead
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.