5

I'm writing a Vulkan Renderer for GWEN (GUI Without Extravagant Nonsense) and having issues getting texture coordinates to match up.

Here's what I should see: Correct Output

And this is what is currently happening: Incorrect Output

The texture displayed in the center is the image we're using for the GUI, I made sure to test render it to a quad to make sure I was loading it properly.

The structure of the GUI appears to be correct which leads me to believe the UV coordinates are incorrect. Specifically the Y coordinate since, from what I can tell, X appears okay.

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);
}

The library builds a vector of vertices then finally renders the vertex buffer each frame.

For reference, here is the same function from the OpenGL Sample Renderer:

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);
}

I understand Vulkan takes texture coordinates from 0.0 to 1.0 which is why I divide u and v by the size of the texture 512 x 512. I also understand Vulkan uses the bottom left corner of the image as 0,0 which is why I flip the Y coordinate. Sadly I still get the incorrect output. I've tried many combinations for texCoord.x and texCoord.y but I can never get the correct output. Does anyone know what I might be doing wrong here?

EDIT Dividing the texture by 128 instead of 512 produced the correct results. I don't understand why this solved my issue though. The texture size is indeed 512x512 units so dividing by 512 should give me a 0.0-1.0 range? Instead I only divide by 128 which is 1/4th the size of the texture to get my 0.0-1.0 range?

vertices.back().texCoord.x = (u / 128);
vertices.back().texCoord.y = (v / 128);
KKlouzal
  • 732
  • 9
  • 32
  • 2
    Is there a possibility that there's a mismatch between your vertex layout in your c++ code, and the vertex description that you're giving to Vulkan? i.e. Maybe `texCoord.x` and `texCoord.y` are correct but some other value is being used by the time the data reaches the vertex shader. – Columbo Nov 12 '19 at 18:44
  • Rerunning the program produces the exact same output. Changing the formula used to produce the `texCoord.x` and `texCoord.y` values will visually change the output therefore the shader is definitely receiving the values we have set for `texCoord.x` and `texCoord.y`. For example, dividing by `256` or `128` instead of `512` visually scales out the texture being applied to the GUI, the UV mapping is just all wrong. – KKlouzal Nov 12 '19 at 18:50
  • 1
    _Vulkan uses the bottom left corner of the image as 0,0._ I found the specification a little bit confusing here. The part [15.1.1.](https://www.khronos.org/registry/vulkan/specs/1.1-khr-extensions/html/chap15.html#textures-texel-coordinate-systems) has a diagram with `t` coordinate pointing up, but the text doesn't mention the direction (though in the older spec it was explicitly stated that the texture origin is top left). More info [here](https://community.khronos.org/t/vulkan-texture-coordinate-on-the-spec/104433/6) and [here](https://github.com/KhronosGroup/Vulkan-Docs/issues/876). – nikitablack Nov 15 '19 at 08:15
  • `texCoord.x = (u / 512.0f)` should normalize the coordinates to `0.0 (left)` -> `1.0 (right)` and `texCoord.y = 1.0f - (v / 512.0f)` should normalize the coordinates to `0.0 (bottom)` -> `1.0 (top)`. Unless there is some other problem further back in the application giving me incorrect UV coordinates at this stage. Could it be that `u1` `u2` `v1` and `v2` need to be reordered inside `Vulkan::DrawTexturedRect`? – KKlouzal Nov 15 '19 at 08:26
  • 1
    `texCoord.y` should be from top to bottom. This is how a texture is sampled in Vulkan. – nikitablack Nov 15 '19 at 09:16

1 Answers1

4

As it turns out Vulkan and OpenGL share the same physical 0,0 position when it comes to UV coordinates. Flipping the y value was unnecessary. Finally what gave me the correct output was to divide the size of the texture by 1/4th or 128 instead of 512. For some reason this produced the correct output and all the UV's lined up properly.

correct formulas:

vertices.back().texCoord.x = (u / 128);
vertices.back().texCoord.y = (v / 128);
KKlouzal
  • 732
  • 9
  • 32
  • 1
    If you're curious to get to the bottom of this, then I'd recommend using [RenderDoc](https://renderdoc.org/) to capture a frame. – Columbo Nov 17 '19 at 09:39