Here, I gained experience with the Vulkan API under assistance of this tutorial by Alexander Overvoorde. In this project, I was able to render different types of shaders at the same time and I implemented PBR for the 3D objects. I also created the primitive shapes like cubes and spheres in 3D and rectangles, rounded rectangles and ellipses in 2D.
To project textures onto different types of objects, I implemented methods that generate the following shapes:
Spheres
Cubes (Beams)
Rectangle
Rounded Rectangle
Ellipse
Meshes
All these shapes own a vertex- and indexbuffer that will be bound to a VkCommandBuffer to be able to draw the shapes. When creating a mesh, we immediately calculate the uv coordinates of each vertex in a 0-1 x and y range. (This is basically unwrapping your mesh in code)
I used nlhomann's JSON Library to load in the 3D and 2D meshes and to load in the 'materials'.
The following things are some of the things Vulkan Graphics Pipeline takes care of.
It determines the viewport and the scissor rectangle which determine what area of the screen is use to render.
More importantly, it also determines and specifies properties of the Rasterization state. Some interesting properties of this are polygonMode (Fill, Line, ...), cullMode and frontFace (CW or CCW).
It also determines the Depth-Stencil state. In our case, we specify the depthbounds to 0-1 and disable the stencil test. It also handles the Pipeline Layout which sepcifies the quite some things itself like pushconstant ranges and Description Set Layouts.
These specify what frame-constant data needs to be pushed for which shaders. In our case we have a puchconstant for the vertex shader, which specifies the size of mesh data (model matrix), and a pushconstant for the fragment shader, which specifies the render properties (Texture mode and Normal map use). The actual pushconstant data needs to be determined right before drawing.
We're using descriptor sets to specify the textures and to specify the view project matrices using a Uniform Buffer.
After creating the Descriptor Pool, we need to create a Descriptor Set Layout. As the name implies, this defines the layout of the Descriptor Sets we will create. It also knows to which shader it needs to be passed. This Descriptor Set Layout is passed to the Graphics Pipeline.
We can create the descriptor sets via the descriptor pool and descriptor set layout that knows the size and type (Uniform Buffer, Image, ...) of the requested descriptor sets.
For each mesh we create their own descriptor sets using the textures. we create a descriptor set per texture. Though, we only need one descriptor set of the view projection uniform buffer, since this counts for the entire meshgroup in the pipeline.
To understand how each type of texture map needs to be interpreted to implement Physically Based Rendering, view the Software Rasterizer.