VulkanTutorial icon indicating copy to clipboard operation
VulkanTutorial copied to clipboard

GLM_FORCE_LEFT_HANDED

Open jjYBdx4IL opened this issue 3 years ago • 13 comments

Is this explained?:

https://github.com/Overv/VulkanTutorial/blob/master/code/27_model_loading.cpp#L1331

I think you need to add

#define GLM_FORCE_LEFT_HANDED

to avoid that "fix".

jjYBdx4IL avatar Jun 24 '21 11:06 jjYBdx4IL

I can't remember where it is explained, but if I recall correctly, this is because OpenGL has not the same image coordinates convention as Vulkan, and GLM was made for GL, so we need to inverse the image coordinate to avoid having an upside-down result

Krenodeno avatar Jun 24 '21 15:06 Krenodeno

Found the explanation: https://github.com/Overv/VulkanTutorial/blob/master/en/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md near the end

Krenodeno avatar Jun 24 '21 15:06 Krenodeno

Exactly. That's what the left hand macro is for.

jjYBdx4IL avatar Jun 24 '21 15:06 jjYBdx4IL

https://chromium.googlesource.com/external/github.com/g-truc/glm/+/HEAD/manual.md#section2_16

jjYBdx4IL avatar Jun 24 '21 15:06 jjYBdx4IL

That can't be the only thing, because if I define GLM_FORCE_LEFT_HANDED and remove the -1 multiplication, I get an upside down image. It's only upright if I proceed to flip the up axis in the glm::lookAt.

Overv avatar Jun 25 '21 17:06 Overv

For example 27 I have reconstructed 3 changes from my own implementation:

Header section:

#define GLM_FORCE_LEFT_HANDED
#include <glm/ext.hpp> // for glm::rotate

At end of loadModel:

    // fix model coordinates
    glm::mat4 rot = glm::rotate(glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f))
        * glm::rotate(glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
    for (Vertex& v : vertices) {
        v.pos = rot * glm::vec4(v.pos, 1.0f);
    }

updateUniformBuffer:

    ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    ubo.view = glm::lookAt(glm::vec3(2.0f, -2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

Though I have to admit that I'm not quite sure why the up-vector has a positive y component. Shouldn't that be negative in Vulkan coords? Probably because the model coords are still upside down in Vulkan coords? I did only rotate the model after import after all.

jjYBdx4IL avatar Jun 25 '21 18:06 jjYBdx4IL

The proper import of the object model and its proper alignment along Vulkan coordinates probably warrants a separate chapter.

jjYBdx4IL avatar Jun 25 '21 19:06 jjYBdx4IL

The model was exported with positive Y coordinates meaning up, so that would be the convention for the view transform in this case.

Overv avatar Jun 25 '21 21:06 Overv

The proper fix to this issue is not to do this at run-time, but instead using a compiler switch for the shaders:

  • -finvert-y for glslc
  • -fvk-invert-y for dxc

The compiler switches will add code that inverts the y-coordinate for outputs of the VS/GS/TE shader stages to comply with the Vulkan coordinate system.

crud89 avatar Jun 26 '21 11:06 crud89

@crud89 Assuming that you mean --invert-y, it doesn't seem to make any difference for me.

Overv avatar Jul 03 '21 14:07 Overv

Going by this article it seems that the solution could be:

  • Tell GLM to use a left-hand coordinate system:
#define GLM_FORCE_LEFT_HANDED
  • Flip the viewport upside down:
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = (float)swapChainExtent.height;
viewport.width = (float)swapChainExtent.width;
viewport.height = -(float)swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
  • Fix the frontFace:
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;

Overv avatar Jul 03 '21 14:07 Overv

@crud89 Assuming that you mean --invert-y, it doesn't seem to make any difference for me.

glslc --help says it's -finvert-y for me. 🤔

It's weird, that it does not work for you. I tried with both (dxc and glslc) and both fixed the issues. Handedness should not influence the y-coordinate, only the z-coordinate (whether positive or negative is pointing forward).

The tutorial flips the sign of the (V)P1,1 matrix. Ultimately this corresponds to multiplying the VP matrix with a matrix that looks like this:

1  0  0  0
0 -1  0  0 
0  0  1  0
0  0  0  1

This transform does nothing else than inverting the y coordinate of the result, so basically this is the same as using the compiler switch.

The article you linked does two things:

  1. Use LH coordinate system, which flips the z-coordinate. That's why it requires you to swap the cull order (since normals now point in the wrong direction).
  2. Negate the sign of the viewport height. Since viewport transform is just a builtin fragment coordinate transform, this does the same as your current matrix-multiplication... or the compiler switch I mentioned.

So there are many fixes to this issue. I personally prefer using the compiler switches, because it appears to be the most portable solution. I am, however, not sure if it might be an issue with glslc and glsl shaders (since I am using hlsl).

crud89 avatar Jul 03 '21 17:07 crud89

glslc --help says it's -finvert-y for me. 🤔

Ah right, I just noticed that my local compiling script was still using glslangValidator.

Overv avatar Jul 04 '21 11:07 Overv