如何正确地将2D图像纹理映射到正二十面体上

7
我正在使用C++编写一个简单的图形引擎,可以表示地球。地球被建模为一个“大地球体”,我计划使用每个顶点的法线来映射它的纹理。为此,我需要从顶点的法线中获取垂直和水平坐标(分别为φ和θ,即纬度和经度)。
以下是实现我的方法的函数代码(进行了编辑),用于在生成新顶点时返回从(0,0)到(1,1)之间的纹理坐标,参数n为法向量:
glm::vec2 Icosphere::getTexCoord(glm::vec3 n)
{
    float theta = (atan2f(n.x, n.z) / PI) / 2.f + 0.5f;
    float phi = (asinf(-n.y) / (PI / 2.f)) / 2.f + 0.5f;
    return glm::vec2(theta, phi);
}

然而,这样做会产生意外的结果。使用此测试图片,我的正二十面体看起来是这样的:

Example 1

Example 2

小白色和红色三角形位于(x,y,z) = (1,0,0)。在第一张图像中,我使用较少的三角面生成了正二十面体(320个),而在后者中,正二十面体是由约82k个三角形生成的。

我怀疑导致这些纹理“故障”的原因是坐标(0,*)(1,*)不被视为相邻。是否有适当的方法来解决这个问题?

NB:这些是我的纹理参数:

/* Configure texture parameters: */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);

我的猜测是,在生成顶点法线集时,存在某种累积的浮点误差。如果我能看一下那段代码,我可能会找到问题所在。getTexCoord函数在我看来是正确的。 - Christopher Oicles
你可以尝试将所有法线的向量和进行计算,看看它是否接近于零。这可能会给你一些关于问题原因的线索。 - Christopher Oicles
我会试试看是否与我的法线有关,但我有疑虑,因为我使用相同的法线来渲染光效,它们看起来非常完美。 - Carles Araguz
我认为问题的原因是由于一些三角形(纹理边缘的三角形)具有两侧顶点(0,y)(1,y)。对于这些情况,在顶点位于不同边缘时,纹理被压缩而不是将纹理边缘视为相邻。这可能是问题的原因吗? - Carles Araguz
你的猜测比我的好。这就解释了为什么具有更大三角形的球体比具有较小三角形的球体更扭曲。之前我错误地认为第一张图片有更多且更小的三角形,所以现在你的理论听起来更好了。 - Christopher Oicles
我现在确信你是对的,因为这些不好的三角形具有10条纹理,所以这些三角形被映射到整个纹理的跨度上,反向映射,因为它们的一个顶点包裹到了相反的一侧。 - Christopher Oicles
1个回答

3
我终于成功地解决了这个丑陋的纹理映射问题。问题确实是由于二十面体中三角形的非均匀方向特性引起的。在一个二十面体(以及一个正二十面体)中,不可能找到一个不与任何三角形相交的原点平面(0,0,0)。如果我们想到地球,这意味着所有的经线都与一些三角形相交。这也意味着在映射纹理时,位于纹理垂直边缘(即具有纹理坐标(1,y)(0,y)的三角形)的三角形将有一些顶点在两侧。当发生这种情况时,纹理从x ≈ 1内插到x ≈ 0,产生了我图片中展示的效果。
为了解决这个问题,我将纹理包裹设置为GL_REPEAT,并强制将一些顶点远离纹理坐标空间(即x> 1)。这使得纹理边缘成为相邻,并完美地映射了纹理。
/* Configure texture parameters: */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Not GL_CLAMP_TO_EDGE.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Not GL_CLAMP_TO_EDGE.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);

结果:

将三角形边缘的纹理坐标进行修改(使用 GL_CLAMP_TO_EDGE)后:

Result1

将纹理参数更改为 GL_REPEAT

enter image description here


4
你是如何检测哪些顶点需要远离纹理坐标空间的?我不确定如何进行检测。 - DCON
我有同样的问题。我的唯一想法是复制“接缝”上的某些三角形顶点,并为复制的顶点赋予不同(>1)的纹理坐标。这是你所做的吗?还是有一种巧妙的方法可以在不重复的情况下解决它? - Wutz
1
自从我实现了这个引擎以来已经过了很长一段时间,但是在接缝处复制顶点对我来说现在似乎不是一个坏主意(就内存而言,在球体中总点数相比于冗余点的数量可能是微不足道的)。事实上,我甚至记不清我的三角形是否与相邻的三角形共用顶点了...! 如果它们在你的情况下确实具有顶点共享,则可以简单地复制与每个_三角形_相关联的接缝纹理数据,而不是将纹理数据设置为单个_顶点_。 - Carles Araguz
感谢 @CarlesAraguz 的回复! 我想知道您说的每个三角形设置纹理数据而不是每个顶点是什么意思。我只知道一种贴图的方法:为每个顶点设置一个纹理坐标,让片段着色器插值。还有其他我不知道的方法吗? - Wutz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接