Introduction
This short tutorial deals with Vulkan’s viewport setup, which differs from the one in OpenGL and other APIs. I’ll try to explain what it takes to get your (OpenGL) scene rendered properly, and how e.g. VK_KHR_MAINTENANCE1
can help you deal with differences across the APIs, something that’s esp. helpful if you try to add Vulkan as a backend to you renderer without wanting to alter your actual scene data.
The problem
Passing your vertex data jus like in OpenGL, using the same state (as the pipeline setup) and shaders as in OpenGL, your scene will likely not display as you’d expect.
Take this demo that is supposed to render the Sibenik Cathedral in Vulkan with the same vertex data and pipeline setup as in a similar OpenGL example:
This was rendered with:
- Y values of the vertex data pointing up
- Back face culling enabled
- Winding order set to counter clockwise
Comparing it with how the scene should be rendered:
It’s easy to see what’s actually wrong here:
- The scene is rendered upside down
- Culling is removing front faces instead of back faces
Different coordinate systems
The cause for this is pretty simple: Vulkan is using a different coordinate system.
The viewport’s origin in OpenGL is in the lower left of the screen, with Y pointing up. In Vulkan the origin is in the top left of the screen, with Y pointing downwards. The latter part explains why our scene is rendered upside down.
Quick and dirty
So a quick and easy fix would be just inverting all your y coordinates, either on the host or in the vertex shader like this:
void main()
{
...
gl_Position.y = -gl_Position.y;
}
Even though that would work, you’d have to add this to every vertex shader, which would be especially annoying when working on a renderer with multiple backends sharing the same shaders.
Flipping the viewport
Luckily Khronos realized that the different coordinate system could possibly impede Vulkan adoption and decided to fix this. Designing an API is not an easy task, especially when it has to cover a wide range of operating systems, platforms and devices.
So after collecting developer feedback, the VK_KHR_Maintenance1
extensions was added for Vulkan 1.0, including, among other API fixes, support for passing negative viewport heights:
Allow negative height to be specified in the
VkViewport::height
field to perform y-inversion of the clip-space to framebuffer-space transform. This allows apps to avoid having to usegl_Position.y = -gl_Position.y
in shaders also targeting other APIs.
Note: The extension has become core with Vulkan 1.1. So if you only target this Vulkan version (and up), you can flip the viewport without having to check for the extension.
So when setting the viewport state we can now simply specify a negative viewport height to flip it along the y axis:
|
|
Note that you also need to change the viewport’s origin (line 6), or you won’t see anything of your scene as the whole viewport is outside of the window.
With this setup you can now use the same shaders, vertex data and pipeline setup as e.g. in your OpenGL app.
Sample
I also have added a new example to demonstrate how flipping the viewport in Vulkan works, how Vulkan compares to e.g. OpenGL, and how the different pipeline states affect the viewport.