Summary
Shaders are a more efficient way of rendering as they speak directly to the graphics card. However, the way in which they process one vertex or one fragment at a time in parallel can initially be confusing and frustrating. Indeed, the mathematics used in them can appear rawer than other operations embedded in scripting languages and API calls, such as those used in the old version of OpenGL, as you, as the programmer, don’t ever see the exact code inside higher-level methods. However, when placed in the shader code, there’s nowhere to hide. The higher-level methods don’t exist and you have to write a lot of the functionality from scratch. For example, whereas in OpenGL versions 2 and lower, calls to gluPerspective()
would set up the projection matrix and automatically affect any meshes in the environment, with shader code you need to keep a copy of the projection matrix, maintain it, and then manually feed it through to the vertex shader where it is multiplied...