从OpenGL到Vulkan规范化UV坐标

5

我正在为 GWEN(GUI Without Extravagant Nonsense) 编写一个 Vulkan 渲染器,并且在尝试将纹理坐标匹配时遇到了问题。

这是我应该看到的: Correct Output

而这是当前发生的事情: Incorrect Output

中心显示的纹理是我们用于 GUI 的图像,我确保渲染到四边形以确认我正确加载它。

GUI 结构似乎正确,这让我相信 UV 坐标不正确。具体来说,是 Y 坐标有问题,因为据我所知,X 看起来还好。

void Vulkan::AddVert(int x, int y, float u, float v)
{
    vertices.emplace_back();

    vertices.back().pos.x = ((float)x / 400) - 1.f;
    vertices.back().pos.y = ((float)y / 300) - 1.f;
    //  Our image is 512x512 px
    vertices.back().texCoord.x = (u / 512.0f);
    vertices.back().texCoord.y = 1.0f - (v / 512.0f);
}

void Vulkan::DrawTexturedRect(Gwen::Texture* pTexture, Gwen::Rect rect, float u1, float v1, float u2, float v2)
{
    //  ToDo: Implement textures through GWEN
    //  The GUI texture is hardcoded for now
    //  Once we're rendering properly i'll expand this function to handle textures
    Translate(rect);

    AddVert(rect.x, rect.y, u1, v1);
    AddVert(rect.x + rect.w, rect.y, u2, v1);
    AddVert(rect.x, rect.y + rect.h, u1, v2);
    AddVert(rect.x + rect.w, rect.y, u2, v1);
    AddVert(rect.x + rect.w, rect.y + rect.h, u2, v2);
    AddVert(rect.x, rect.y + rect.h, u1, v2);
}

该库构建了一个顶点向量,然后在每一帧渲染顶点缓冲区。

以下是OpenGL示例渲染器中相同函数的代码:

void OpenGL::AddVert( int x, int y, float u, float v )
{
    m_Vertices[ m_iVertNum ].x = ( float ) x;
    m_Vertices[ m_iVertNum ].y = ( float ) y;
    m_Vertices[ m_iVertNum ].u = u;
    m_Vertices[ m_iVertNum ].v = v;
    m_Vertices[ m_iVertNum ].r = m_Color.r;
    m_Vertices[ m_iVertNum ].g = m_Color.g;
    m_Vertices[ m_iVertNum ].b = m_Color.b;
    m_Vertices[ m_iVertNum ].a = m_Color.a;
    m_iVertNum++;
}

void OpenGL::DrawTexturedRect(Gwen::Texture* pTexture, Gwen::Rect rect, float u1, float v1, float u2, float v2)
{
    GLuint* tex = ( GLuint* ) pTexture->data;

    // Missing image, not loaded properly?
    if ( !tex )
    {
        return DrawMissingImage( rect );
    }

    Translate(rect);
    GLuint boundtex;
    GLboolean texturesOn;
    glGetBooleanv( GL_TEXTURE_2D, &texturesOn );
    glGetIntegerv( GL_TEXTURE_BINDING_2D, ( GLint* ) &boundtex );

    if ( !texturesOn || *tex != boundtex )
    {
        Flush();
        glBindTexture( GL_TEXTURE_2D, *tex );
        glEnable( GL_TEXTURE_2D );
    }

    AddVert(rect.x, rect.y, u1, v1);
    AddVert(rect.x + rect.w, rect.y, u2, v1);
    AddVert(rect.x, rect.y + rect.h, u1, v2);
    AddVert(rect.x + rect.w, rect.y, u2, v1);
    AddVert(rect.x + rect.w, rect.y + rect.h, u2, v2);
    AddVert(rect.x, rect.y + rect.h, u1, v2);
}

我了解Vulkan使用从0.0到1.0的纹理坐标,这就是为什么我要将和v除以纹理的大小512 x 512。我也明白Vulkan使用图像的左下角0,0,这就是为什么我要翻转Y坐标。不幸的是,我仍然得到了错误的输出。我已经尝试了很多组合来改变texCoord.xtexCoord.y,但我从未得到过正确的输出。有人知道我在这里做错了什么吗? 编辑 通过将纹理大小除以128而不是512,我得到了正确的结果。我不明白为什么这样可以解决我的问题。因为纹理大小确实是512x512单位,所以除以512应该给我一个0.0-1.0的范围?但我只除以了128,这是纹理大小的四分之一,却能得到0.0-1.0的范围?
vertices.back().texCoord.x = (u / 128);
vertices.back().texCoord.y = (v / 128);

2
你的C++代码中的顶点布局与你提供给Vulkan的顶点描述之间是否存在不匹配的可能性?也就是说,也许texCoord.xtexCoord.y是正确的,但在数据到达顶点着色器时使用了其他值。 - Columbo
重新运行程序会产生完全相同的输出。更改用于生成texCoord.xtexCoord.y值的公式将在视觉上更改输出,因此着色器绝对接收我们为texCoord.xtexCoord.y设置的值。例如,除以256128而不是512会在视觉上扩大应用于GUI的纹理,但UV映射完全错误。 - KKlouzal
1
Vulkan使用图像的左下角作为0,0。我发现规范在这里有点令人困惑。第15.1.1节有一个带有指向上的t坐标的图表,但是文本没有提及方向(尽管在旧规范中明确说明纹理原点是左上角)。更多信息请参见此处此处 - nikitablack
texCoord.x = (u / 512.0f) 应该将坐标规范化为 0.0(左) -> 1.0(右),而 texCoord.y = 1.0f - (v / 512.0f) 应该将坐标规范化为 0.0(底部) -> 1.0(顶部)。除非在应用程序的更早阶段存在其他问题导致我在此阶段得到不正确的 UV 坐标。可能需要在 Vulkan::DrawTexturedRect 中重新排序 u1u2v1v2,这会有影响吗? - KKlouzal
1
texCoord.y 应该从上到下。这是在 Vulkan 中采样纹理的方式。 - nikitablack
1个回答

4
正如所言,当涉及到UV坐标时,Vulkan和OpenGL共享相同的物理0,0位置。翻转y值是不必要的。最后,给我正确输出的是将纹理的大小除以1/4th128而不是512。出于某种原因,这样产生了正确的输出,并且所有的UV都正确地对齐了。
正确的公式:
vertices.back().texCoord.x = (u / 128);
vertices.back().texCoord.y = (v / 128);

1
如果你想深入了解这个问题,我建议使用RenderDoc来捕获一帧。 - Columbo

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