Using the above answers I have refactored my code and added some functionality.
Made all floats passed by value instead of reference(answer of @1201ProgramAlarm)
Removed CameraController class and moved logic to Camera base class(answer of @G. Sliepen)
More specific includes, instead of including glm.hpp now includes only needed definitions like ...
Is the API easy to understand/implement by another user
It depends on how it will be used I think. Maybe the API is just what you need in your project, but another user might want to have a function like lookAt(const glm::vec3 &position) to point the camera at a specific point, or perhaps wants to set the field of view of a perspective camera as an ...
Your header files seem to be including unnecessary things, apparently for convenience. A header should only include the files required for that header to compile. Anything else just adds to the compile overhead for files that include the header but don't need those details.
float parameters should be passed to functions by value, not reference. Passing ...
Two things that I can see that I think would improve your structure a lot:
Keep the shader sources in separate files (I like to have them in /res/shaders with a .glsl extension) and load them in. You could have a class called ShaderProgram with methods load_shader and that also links up the shader program and removes the compiled shaders when linked.
Hello and welcome to CodeReview!
A random list of observations:
For your members,
self.vert_coords = 
can be (assuming it's a list of floats)
self.vert_coords: List[float] = 
and so on. This helps for a few reasons - smart IDEs will notice if you're using the member with an unexpected type and can warn you; autocomplete is better; ...