Vulkan glTF 2.0 C++ phyiscal based rendering

I have released the first working version of a separate (from the examples) Vulkan physical based rendering example that uses the glTF 2.0 model file format.

The repository can be found at

glTF is a royalty free format specification by the Khronos Group and is a new format for 3D models gaining lots of traction. With version 2.0 it also added several PBR extensions and definitions.

I decided to make this a stand-alone project instead of “just” another example in my Vulkan C++ example repository to make it easier getting into the code. While lots of people seem to learn from my examples they contain a lot of abstraction, and for this example I wanted the code to be very readable and verbose on purpose so those interested in doing PBR with Vulkan and glTF 2.0 get a closer look at the actual code without that much abstraction as was the case for my examples.

So while it still contains a few base classes from my examples, these have been stripped down (e.g. no more initializers) and the main class is pretty verbose and doesn’t do much Vulkan abstraction. I hope this makes it easier to actually get into the relevant Vulkan parts.


  1. Nice demo.

    Just one thing: the latest NVIDIA Vulkan beta driver 388.84 is mandatory.
    390.77 (API 1.065) quits with:
    “Das Programm “[14996] VulkanPBR.exe” wurde mit Code -9 (0xfffffff7) beendet.”

    1. That’s odd, current SDK version should work fine with older drivers. At least fro me on Intel (Linux) and mobile, where supported API level is even lower. Any details on the error? Can you run with validation layers?

  2. Yikes! Now it crashes also with the beta driver.
    So its not related to the API version.

    Ausgelöster Ausnahmefehler: Lesezugriffsverletzung
    “**this**” war “nullptr”.

    > VulkanPBR.exe!gli::storage_linear::extent(unsigned __int64 Level) Zeile 69 C++ Symbole wurden geladen.
    VulkanPBR.exe!gli::texture::cache::cache(gli::storage_linear & Storage, gli::format Format, unsigned __int64 BaseLayer, unsigned __int64 Layers, unsigned __int64 BaseFace, unsigned __int64 MaxFace, unsigned __int64 BaseLevel, unsigned __int64 MaxLevel) Zeile 231 C++ Symbole wurden geladen.
    VulkanPBR.exe!gli::texture::texture(const gli::texture & Texture, gli::target Target, gli::format Format, const glm::vec & Swizzles) Zeile 82 C++ Symbole wurden geladen.
    VulkanPBR.exe!gli::texture_cube::texture_cube(const gli::texture & Texture) Zeile 16 C++ Symbole wurden geladen.
    VulkanPBR.exe!vks::TextureCubeMap::loadFromFile(std::basic_string<char,std::char_traits,std::allocator > filename, VkFormat format, vks::VulkanDevice * device, VkQueue_T * copyQueue, unsigned int imageUsageFlags, VkImageLayout imageLayout) Zeile 299 C++ Symbole wurden geladen.
    VulkanPBR.exe!VulkanExample::loadAssets() Zeile 246 C++ Symbole wurden geladen.
    VulkanPBR.exe!VulkanExample::prepare() Zeile 1357 C++ Symbole wurden geladen.
    VulkanPBR.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * __formal, char * __formal, int __formal) Zeile 1456 C++ Symbole wurden geladen.
    [Externer Code] Frame mit Anmerkungen

    — d:\vulkan-gltf-pbr\external\gli\gli\core\storage_linear.inl —————-

    inline storage_linear::extent_type storage_linear::extent(size_type Level) const
    00007FF70813EF50 mov qword ptr [rsp+18h],r8
    00007FF70813EF55 mov qword ptr [rsp+10h],rdx
    00007FF70813EF5A mov qword ptr [rsp+8],rcx
    00007FF70813EF5F push rdi
    00007FF70813EF60 sub rsp,50h
    00007FF70813EF64 mov rdi,rsp
    00007FF70813EF67 mov ecx,14h
    00007FF70813EF6C mov eax,0CCCCCCCCh
    00007FF70813EF71 rep stos dword ptr [rdi]
    00007FF70813EF73 mov rcx,qword ptr [this]
    GLI_ASSERT(Level >= 0 && Level Levels);
    00007FF70813EF78 cmp qword ptr [Level],0
    00007FF70813EF7E jb gli::storage_linear::extent+40h (07FF70813EF90h)
    00007FF70813EF80 mov rax,qword ptr [this]
    00007FF70813EF85 mov rax,qword ptr [rax+10h]
    00007FF70813EF89 cmp qword ptr [Level],rax
    00007FF70813EF8E jb gli::storage_linear::extent+5Ch (07FF70813EFACh)
    00007FF70813EF90 mov r8d,45h
    00007FF70813EF96 lea rdx,[string L”d:\\vulkan-gltf-pbr\\e”… (07FF7081D3250h)]
    00007FF70813EF9D lea rcx,[string L”(Level >= 0 && Level”… (07FF7081D33C0h)]
    00007FF70813EFA4 call qword ptr [__imp__wassert (07FF70823CAB8h)]
    00007FF70813EFAA xor eax,eax

    return glm::max(this->Extent >> storage_linear::extent_type(static_cast(Level)), storage_linear::extent_type(1));

    1. The environment cube map is the first asset that gets loaded. Make sure you’re running your binary from the right directory relative to the data directory.

  3. That’s it.
    VS 2017 put the executable into an extra bin folder.


    Moving the executable a level up fixed it.


  4. Yeah, that’s a “speciality” of VS. Last time I checked there was no easy way using CMAKE to tell VS the default output directory, so you have to manually change that one of the directory the binary is executed in. I’ll check and see if it’s possible to change this with CMAKE.

Leave a Reply

Your email address will not be published. Required fields are marked *