如何在一组戈德堡多面体面上画出边框轮廓?

How to draw a border outline on a group of Goldberg polyhedron faces?

我有一个程序生成的戈德堡多面体。我想围绕一组类似于下图的“面”(我们称它们为图块)绘制轮廓效果,最好不要生成两个网格,在顶点着色器中进行缩放。有人可以帮忙吗?

我的假设是使用缩放版本的图块写入模板缓冲区,然后重绘这些图块比较模板来绘制轮廓(通常用于这种效果),但我不能来想出一个优雅的解决方案来缩放图块。

到目前为止,我最好的想法是为每个边缘顶点(蓝色)获取相邻图块(下面的绿色)的中心点,然后将顶点移向它们,并根据存在的数量加权,这将留下内部的未修改,外部的向内移动。我认为这原则上可行,但我需要生成两个网格,因为我无法在顶点着色器中以这种方式缩放(据我所知)。

如果相关的话,这就是多面体的构造方式。每个图块都是一个单独的对象,表面用一个中心点进行三角剖分,并且在多面体的原点(也是图块对象的原点)处有另一个点。这只是为了让图块可以均匀缩放并从多面体中突出而不产生间隙或重叠。

在此先感谢您的帮助!

编辑:

jsb 的回答是对这个问题的简单而优雅的解决方案。我只是想添加一些额外的信息,以防其他人遇到同样的问题。

首先,这是我用来计算这些 UV 的 C# 代码:

// Use duplicate vertex count (over 4)
var vertices = mesh.vertices;
var uvs = new Vector2[vertices.Length];
for(int i = 0; i < vertices.Length; i++)
{
    var duplicateCount = vertices.Count(s => s == vertices[i]);
    var isInterior = duplicateCount > 4;
    uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}

请注意,这是有效的,因为我没有在我的原始网格中焊接任何顶点,所以我可以通过查找重复的顶点来计算相邻的三角形。

您也可以像这样对三角形进行计数(这适用于合并的顶点,至少对于 Unity 的网格数据的布局方式):

// Use triangle count using this vertex (over 4)
var triangles = mesh.triangles;
var uvs = new Vector2[mesh.vertices.Length];
for(int i = 0; i < triangles.Length; i++)
{
    var triCount = triangles.Count(s => mesh.vertices[s] == mesh.vertices[triangles[i]]);
    var isInterior = triCount > 4;
    uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}

下面的问题。在我的用例中,我还需要为不规则的瓷砖图案生成轮廓,如下所示:

我在原文中忽略了这一点post。 Jsb 的回答仍然有效,但上面的代码将无法正常工作。如您所见,当我们有一个仅由一条边连接的图块时,连接的顶点仅“共享”2 个内部三角形,因此我们得到一条“外部”边。作为对此的解决方案,我沿着瓷砖的外边缘创建了额外的顶点,如下所示:

我通过计算沿原始外部图块顶点 (a + (b - a) * 0.5) 之间的向量的中点并在此处插入一个点来完成此操作。但是,如您所见,简单的“重复顶点 > 4”不再适用于确定哪些瓷砖位于外部。

我的解决方案是以特定顺序缠绕顶点,所以我知道每个第 3 个顶点都是我沿着边插入的顶点,如下所示:

Vector3 a = vertex;
Vector3 b = nextVertex;
Vector3 c = (vertex + (nextVertex - vertex) * 0.5f);
Vector3 d = tileCenter;
CreateTriangle(c, d, a);
CreateTriangle(c, b, d);

然后修改 UV 代码以测试这些顶点的重复项 > 2(每三个顶点从 0 开始):

// Use duplicate vertex count
var vertices = mesh.vertices;
var uvs = new Vector2[vertices.Length];
for(int i = 0; i < vertices.Length; i++)
{
    var duplicateCount = vertices.Count(s => s == vertices[i]);
    var isMidPoint = i % 3 == 0;
    var isInterior = duplicateCount > (isMidPoint ? 2 : 4);
    uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}

这是最终结果:

谢谢 jsb!

避免第二个网格的一个选项是纹理化: 假设您在三角形顶点上定义 1D 纹理坐标,如下所示:

渲染网格时,使用这些坐标在定义内部和边框颜色的一维纹理中查找:

当然,除了使用纹理,您还可以通过阈值化纹理坐标在片段着色器中实现此行为,从概念上讲:

if (u > 0.9)
    fragColor = white;
else
    fragColor = gray;

要更新轮廓,您只需要上传一组新的 tex 坐标,轮廓上的顶点仅为 1,其他地方为 0。

根据您是希望轮廓仅延伸到所选区域的内部还是对称地延伸到边界的两侧,您需要指定 tex 坐标 per-corner 或 per-vertex, 分别.