The reason I think it deserves to be called "arcane" is that, to even reach an RGB triangle (the shader equivalent of "Hello World"), you have to have dozen different things working all at once:
- Writing / compiling the shaders (vertex and pixel/fragment, at least) - Loading the shaders - Describing the vertex data - Creating vertex data - Describing constant buffers - Creating constant buffers for vertex + world/view/perspective - Filling all the buffers with data - Giving the right data for view / perspective matrices (the camera, basically) - Calling the rendering of a mesh - Getting the output to draw on the screen - + a ton of other drawing descriptions I don't even remember, like raster descriptions
For all of those tasks there is an insane amount of boilerplate that needs to be written. This is okay in itself, since it's all stuff that's worth tweaking for various use cases, but it creates a pretty insane barrier to entry. Not everyone needs to learn all the quirks of setting up vertex buffer descriptions right up front. I personally wasn't going to read a huge DirectX bible just to see how to get started.
It's a problem very much deserving of a copy+paste solution, with some clear instructions on how to tweak for individual needs, but most tutorials (MSDN or otherwise) concentrated on specific features or were in a series building on an already opinionated codebase / pipeline. And if any part of it doesn't work, and you'll be second guessing yourself and asking a ton of questions with every line of code, you have no real way of knowing what went wrong.
However, once I had a pipeline set up (and then started organizing it in a way that it managed itself and could host various shader types) I started having a lot of fun. I'm working on a 3D NES emulator [http://n3s.io] and felt super inventive when I found a way to get the models to render using the NES palette.
It is a lot to learn at once, but no more than web dev where you are learning html and databases and HTTP and god knows what else at once. And like you said once you grok it, it's pretty easy.
Graphics programming terrified me for the longest time because people acted like it was dark magic and hard. Figuring this stuff out slowly but surely it shocks me how little there is to it and I wish someone would have told me sooner.
I've gotten rendering a cube down to an art (gotta start somewhere). You're talking ~100 lines of JS and double that of C++ but no more than that in any decent language. And that's a cube (or multiple) and a basic camera.
A library like regl shows how simple this stuff can get without lots of boilerplate.
https://github.com/regl-project/regl/blob/gh-pages/example/b...
Boom a triangle in 40 lines, but the underlying process of vertices, buffers, shaders, etc is apparent. Cool stuff.
Those asking about Vulkan - Vulkan only increases the boilerplate by design.
Now that I am becoming old, I have started to realize that anyone who creates a feeling of dark magic around almost any programming subject probably doesn't understand the subject very well themselves and has some sort of self-confidence issue that makes them portray whatever it is that way to people who know less than they do.
In the graphics world it's been the other way around. Earlier libraries were higher level, abstracted away a lot, letting you get to "hello world" very quickly. In ancient OpenGL 1.x immediate mode, "Hello triangle" is a handful of lines of code (minus the non-portable window management stuff). As we've moved to vertex buffers, moved away from the fixed function pipeline, less and less is abstracted for you and you have to deal with more and more granular constructs, more and more quirks, and more and more boilerplate. I haven't even started coding with Vulkan, but from what I know about it, any application I'd want to move over to it is probably going to quadruple in terms of line count, complexity, and "things to deal with".
On a more or less modern GPU you won't find those registers. The GPU reads a stream of commands instead of polling its registers. And the most primitive command is DRAW, which means "run the current vertex shader for the given index range, collect its output, rasterize the primitives made from the output and run the current pixel shader on the resulting pixels". So in order to draw anything it's required to have a command stream set up, two shaders set up and necessary data bound to both shaders. Naturally, there is no place for glVertex() in this setup.
There are high level libraries, which abstract the hardware but it's not the purpose of the OpenGL. It's only "abstracting" hardware in the sense that it works same on different models of GPU but it tries to expose as much of the hardware as possible in a model-independent manner.
This is much alleviated when you use a wrapper around OpenGL (e.g. Rust's `gfx`), but most people start with raw OpenGL.
Ironically there's a bunch more debuggers that work with OpenGL as opposed to Vulkan(or know how to debug you're way out of a blank screen by tweaking shaders/api calls to eliminate things).
Very easy to identify things like misaligned data, etc, that would be impossible to find otherwise.
(This feature broke on my desktop, though, and I still can't fix it for the life of me, but I'm past that point in my project development anyway.)
Once you understand that stuff, everything becomes much more straightforward. You still have a lot of "paperwork" to do because you have to set up the whole pipeline to render anything, but at least then you can see the whole picture.
That was my biggest problem when I did some OpenGL work a few years ago. It takes a while to understand what OpenGL really is and what it isn't.