Part of the confusion is the confusing terminology. "Shader" is a bad name. How do you "shade" a vertex? That implies color, when in fact vertex "shading" is really about deforming the position of vertices. It has nothing to do with color!
"Vertex program" is a better term.
That brings us to "pixel shader." That's actually a good name in order for beginners to learn the concept, but it's imprecise. OpenGL insists on calling it a "fragment program" because with certain forms of antialiasing, there are multiple "fragments" per pixel. "Program" is also a better name than "shader" because there are things you can do per-pixel other than change the color. For example you could change the depth written to the Z-buffer, or you could cause the pixel to be skipped based on some criteria, like whether the texture color is pink.
Anyway, it's just a tiny program that executes either per-vertex or per-pixel. For example you could write a vertex program which moves each vertex in a sinewave pattern based on time. Or you could write a fragment program to change the color of each pixel from red to green and back based on time.
Then there are more advanced/recent concepts like a "geometry program," which lets you generate triangles based on vertices or edges.
Sometimes I wonder if it's overly complicated, or if the problem domain is just complicated. It took me years as a kid to finally grok this, but once I learned it, it turned out to be very simple. Honestly it wasn't until I got up enough courage to sit down with the OpenGL specs and read through them that everything clicked. They're dry reading but not difficult.
Traditionally, lighting was calculated per vertex, not per pixel, and the results of those calculations were simply interpolated across the surface of the triangle. A "shader" is not the most general term they could have used for a GPU program, but you very well can "shade" a vertex.
Edit: Is this seriously being downvoted? "How do you "shade" a vertex? That implies color, when in fact vertex "shading" is really about deforming the position of vertices. It has nothing to do with color!" is factually inaccurate because you can, and many games do, calculate color and lighting information per-vertex.
Actually, the per-vertex color is passed into the pixel shader, which then decides what to do with it. The vertex shader itself has nothing to do with color except calculating an initial value and passing it to the pixel shader. The pixel shader can be set up to interpret that value as anything. It could make it a grayscale color, it could invert it, it could use only one of the four channels, etc.
There used to be no pixel shaders, which is why vertex shaders had to be used to do shading. That was circa 2004 though. EDIT: This paragraph is incorrect, see comment below.
Also, the whole idea of "color" in a vertex shader is mistaken. There is only one color: the RGB that ultimately shows up on the screen. Until then, there are values which are passed from the main application source code to the vertex / pixel shaders, which then decide what to do with those values. Sometimes the pixel shader interprets them as a color, but they're really just floating point numbers. That may seem like a silly distinction, but again, if your mental model is incorrect as a newcomer then you're going to have a hard time.
If you go back to what is arguably the earliest hardware that at all resembled the modern 3D pipeline (it predates DirectX, and the DirectX pipeline was greatly inspired by it, according to some article I read on here a while back by one of the guys responsible for the DirectX project), the original Play Station, it mostly consisted of two parts: a "geometry engine" embedded in the CPU which acted like a cross between an SIMD unit and a modern vertex shader ^(it took as inputs a vertex, a light vector, and a transformation matrix, and produced a transformed vertex with an associated color according to a basic diffuse lighting calculation), and a "dumb" rasterizer chip (which knew nothing about 3D, it just took basic 2D/"screenspace" vertex coordinates, color values, and texture coordinates, and naively combined them with no regard to perspective correctness). There was a clear distinction here where the "vertex shader" did the "smart" work to produce a color based on a simple lighting model, and the "pixel shader" did absolutely nothing but interpolate whatever vertex colors and texture values that had been supplied to it, then combine them straightforwardly to produce a final pixel color.
Similarly, if you look at early, pre-shader PC graphics cards, like the Voodoo, these cards (kinda like the Play Station) had nothing resembling a vertex shader, and had no knowledge of lighting calculations. They required the host CPU (acting as our "vertex shader") to provide them with completely lit and transformed vertices that they could then rasterize by naively interpolating and combining whatever color and texture coordinate values the host program had supplied to them.
Given this history, I contest the idea that it's clearly the pixel shader's responsibility to deal with all color and lighting calculations, and the vertex shader's responsibility is merely to geometrically transform vertices in space. I still agree that "shader" is a stupid and confusing term (I was learning about this stuff for the first time not long ago, and realizing that "shaders" were just GPU programs was an "aha! moment" for me too), I'm just disagreeing with the first paragraph of your original post.
^ it could do other stuff, like transform matrices to implement a basic matrix stack, but that run-on sentence was already running on long enough...
Thanks for the interesting background. I am surprised to learn that the ps1 didn't use perspective correct texturing. Textured rectangular surfaces like walls look terrible without it.
"The first shader-capable GPUs only supported pixel shading, but vertex shaders were quickly introduced once developers realized the power of shaders"
I actually very much disagree with your point about names. I write shaders when I have to, I'm not very good at it, but it feels fundamentally very broken, much as I picture the days of 16 bit x86 assembly (memory segmentation?).
There are arbitrary length, register etc limits per DX version, there's no clean cross platform method of writing shaders, the documentation is fragmented, vague, for the wrong platform or non existent.
Sure, the naming could be better, but page one of a decent textbook should set you straight. The other issues, not so much. Of course, that's more or less the price you pay for being on the bleeding edge of performance.
Oh! You're right, I was mixed up about the history. Sadly my mixup will probably detract from my other, non-history points as well. Thank you for correcting me.
I write shaders when I have to, I'm not very good at it, but it feels fundamentally very broken, much as I picture the days of 16 bit x86 assembly (memory segmentation?).
There are arbitrary length, register etc limits per DX version, there's no clean cross platform method of writing shaders, the documentation is fragmented, vague, for the wrong platform or non existent.
This is exactly why I push people to try using OpenGL and avoid Direct3D. All of those problems are D3D problems, not shader problems.
GL has no arbitrary length limits, and it has extremely accurate and thorough documentation. If a program is too complex to execute properly on the given hardware, then it's executed properly in software. Some see that as a terrible thing, and sometimes it is, but in today's mega-GPU world it's becoming increasingly rare to write shader programs that are so complicated that they have to be emulated in software by the driver. Getting an accurate result seems much better.
The limits are hardware limits. DX version is common shorthand for rough hardware generation. If you develop against the same shader model versions in GLSL, you'll have exactly the same limits.
Each video card has a different limit. If you develop against GLSL, you have the limit of whatever videocard you're using, which is substantially different from what DX would have you believe its limit is. GPUs are capable of more than what Microsoft would have you believe they're capable of.
The limits that you're referring to are artificial, because Microsoft mandates that if a pixel shader has more than N instructions then it shouldn't be allowed to compile, regardless of what the videocard is actually capable of doing. It's confusing, and the reason they decided to do it that way was for compatibility across a wide variety of hardware.
The reason it's not called vertex program is because the pipeline is defined in terms of stages (vertex -> tessellation control -> tessellation evaluation -> geometry -> rasterizer -> fragment). Together they are a program. When talking about this structure the choice that OpenGL made is:
"A shader program consists of a vertex shader (VS), tessellation control shader (TS control), tessellation evaluation shader (TS eval), geometry shader (GS) and fragment shader (FS)."
You could instead say this, but it would be confusing:
A shader program consists of a vertex program, tessellation control program, tessellation evaluation program, geometry program and fragment program.
And it would even get more confusing if you drop the first shader:
"A program consists of a vertex program, tessellation control program, tessellation evaluation program, geometry program and fragment program."
So for the sake of it being easy to talk about, a (shader) program is the whole thing, whenever somebody talks about a "program" it's the whole assemblage. And when somebody talks about a shader, it means one of the programs tied to a stage.
Yeah, this has the side effect of making it almost impossible for newcomers to have any clue what the heck is going on until they have several months of (painful) experience trying to figure out each piece.
It doesn't have to be this complicated. Humans just made it that way. I'm just trying to make sure everyone understands that there's nothing mysterious or even especially interesting about these terms. It's complicated like an internal combustion engine is complicated, not like math.
Sure, it did for me. I think mental models are important, and that it's kind of dopey to call something by a confusing name. (I'm looking at you, physics!) But on the other hand, I'm not very smart, and I had to spend most of my time on gradeschool instead of learning shaders. It was also before wonderful resources like stackoverflow/HN/reddit/etc, so there weren't a lot of people to help clarify my mental model.
As I've gotten older it's become easier to think abstractly and accept that names sometimes have nothing to do with what things are. But when you're first starting out, it's natural to want to visualize everything you learn as what it sounds like.
If shaders are hard, it's mostly because they require a full understanding of 3D math, the complete graphic pipeline, texture sampling, image manipulation techniques and how it all comes together before you can actually start doing anything useful; so the learning curve has a slope of 'wat' to almost all newcomers.
If anything, naming is just a nuisance.
they require a full understanding of 3D math, the complete graphic pipeline, texture sampling, image manipulation techniques and how it all comes together before you can actually start doing anything useful
Hm, not really. I learned more from messing with working demos and prototypes than studying theory. But YMMV.
It's absolutely true that the whole pipeline is very intimidating for newcomers, though.
I love that phrase... "the learning curve has a slope of 'wat'."
I don't personally see why this is so confusing. I think that if you're writing shaders you can probably understand that 'program' is a somewhat recursive term. A program is made up of other programs (a function or a module is a program as well).
But even then, you could call them functions (which is practically a synonym for program in a sense, and also obviously recursive, in that functions call other functions).
Either would at least be less obviously 'wrong' in the sense GP meant it.
But I also think "kernel" is a stupid term for GPGPU programs, and definitely increases the barrier to learning what isn't really all that complicated of a thing (at least not at the intro level).
Shader is a good name, because it implies what one of the main purposes of the code is: to apply lighting (shading) to the stuff.
How do you shade a vertex? Read about Gouraud shading. The idea is to do the expensive lighting calculation only at the vertices, and interpolate in-between. The "vertex shader" would calculate the shading at the vertices.
Obviously, you can use a vertex shader to do things other than shading, but "shader" is just a name, not a definition of all things this program can do.
I found the name confusing the first time I encountered it. At first, I assumed it only had something to do with applying fancy lighting effects. Only after studying things further did it become obvious that a shader is responsible for doing 3d->2d projection and pixel filling - and you need them even if you only want to draw solid color shapes!
"Anyway, it's just a tiny program that executes either per-vertex or per-pixel."
Thank you. I have never understood the concept of a <thing>-shader; I just knew they were useful and somehow caused neat effects. Even having pasted some shader code into a program once (for a barrel distortion for my rift), I never thought much about them, likely because of the opacity of the name. Just ... magic.
Your description has just made the entire concept click. I'm now actually interested in learning about them, because it's such a simple, sensible idea. Thanks.
side-note: I wish the world had more one-sentence intuitive sum-ups of jargon-laden concepts, even if they are a little 'leaky' as abstractions. Just to give people disconnected from the topic a place to start thinking about it.
I just want to say that you're correct, I've had this problem as well. As a non-native speaker for some reason it also didn't occur to me that the "shade" in shader has anything to do with color. It's just one of those things you can misunderstand. The way you explain it is much easier to understand.
I think all the people defending the term are all quite invested in the area already, so it's obvious to them.
"Shader" was a term I was confused about at first when I was learning WebGL. In fact, in the WebGL world, it seems like one is expected to come from an OpenGL background and be familiar with whatever popular 3rd party libraries are en vogue. I never found a thorough guide that didn't presume one of those two things, so I built one:
To me, the thing with shaders always is: I need them at some stage in a project. I have used them before, but too long ago to remember, so I need to look up most it. I work with it a for a few weeks. That part of the project is done and I move on.
A year later, a new project comes along. It requires shaders. I don't remember how to use then and have to look it up again...
Have been going through those to learn how to build out the rendering for my game framework. Awesome stuff! Your tutorials have saved me a lot of grokking time.
What shader languages are used in cross-platform games? PS3 uses Cg, PS4 PSSL, Xbox & Windows use HLSL and OpenGL on Mac/Linux/Smartphones/Tablets use GLSL. Are there converters from one to the other language? How do big engines like Unreal oder CryEngine handle this?
As someone who is not well-versed in graphics or graphics programming, this was a very well-explained primer. I very thoroughly enjoyed it, and learned something in the process!
While I appreciate the effort, I think this primer is lacking what most other shader tutorials also lack: any information on how to actually do something useful. Shaders are used because they are faster than the cpu. So why isn't everything done with shaders? Because of limitations. So what are those? Most GPU tutorials only include examples of the form
FragColor = <some algebraic expression containing x & y>
That's nice, but hardly useful. To do anything of worth, I would need data from the CPU. How do I do that? What are the most common bottlenecks? What are some ways around the limitation of working with one fragment at a time? Those are the sort of questions I would like to see answered in a primer. A sort of "GPU introduction for competent CPU devs" - any recommendations?
Bottlenecks and multi-sample output are complex subjects, and also uncommon for a beginner to deal with.
Nevertheless, gonna just pimp my own tutorials since they also cover practical implentations, like blurs for desktop and mobile, normal mapping for 2D games, vignettes, etc.
https://github.com/mattdesl/lwjgl-basics/wiki/Shaders
"Vertex program" is a better term.
That brings us to "pixel shader." That's actually a good name in order for beginners to learn the concept, but it's imprecise. OpenGL insists on calling it a "fragment program" because with certain forms of antialiasing, there are multiple "fragments" per pixel. "Program" is also a better name than "shader" because there are things you can do per-pixel other than change the color. For example you could change the depth written to the Z-buffer, or you could cause the pixel to be skipped based on some criteria, like whether the texture color is pink.
Anyway, it's just a tiny program that executes either per-vertex or per-pixel. For example you could write a vertex program which moves each vertex in a sinewave pattern based on time. Or you could write a fragment program to change the color of each pixel from red to green and back based on time.
Then there are more advanced/recent concepts like a "geometry program," which lets you generate triangles based on vertices or edges.
Sometimes I wonder if it's overly complicated, or if the problem domain is just complicated. It took me years as a kid to finally grok this, but once I learned it, it turned out to be very simple. Honestly it wasn't until I got up enough courage to sit down with the OpenGL specs and read through them that everything clicked. They're dry reading but not difficult.