In a slightly different direction, I'll just push on what ShootMyMonkey was saying
In the old days there were no shaders. However we still had textures. Back then, the hardware could render a surface with a texture on it. Each vertex stored a coordinate to lookup the texture. If we wanted to do something fancy with the texture, we could enable (say) sphere mapping, or we modify a matrix that effected texture lookups. This is how you built an effect. Thats why there used to be loads of swirling, scaling and scrolling 'effect' textures used. Quake3 used these everywhere.
We also started combining multiple textures. Light maps are a good example. You have the surface texture (decal), and then you have a lightmap. Black in the lightmap = black onscreen, white in the lightmap = bright on screen. This is called modulation, basically, multiplication.
You would set texture unit 1 to your decal, texture unit 2 to your lightmap, and then set a texture unit blend mode to modulation.
Now days we have shaders, which we write. All this legacy hardware has gone.
So, this is how the previous situations change:
If we want to lookup a texture, the 'flat and bright' look, we can simply do this:
(*SIMPLIFIED*)
vertex shader:
Code:
output.textureCoordinate = vertex_input.textureCoordinate;
pixel shader:
Code:
return tex2D(texUnit0, textureCoordinate.xy);
That will run very fast.
But... We want to do some fancy scaling texture like Quake3 did?
Just modify the vertex shader:
Code:
output.textureCoordinate = vertex_input.textureCoordinate * scale;
(where scale is controlled in the game code)
how about lightmapping?
pixel shader:
Code:
return tex2D(texUnit0, textureCoordinate.xy) * tex2D(texUnit1, lightMapTextureCoordinate.xy);
So at each pixel, texture 1 is loaded (decal), then multiplied by texture 2 (the light map). It's just code.
We aren't turning on special features anymore.
In this case, the possibilities of what can be done with the hardware increase massively.
Think of any game that does character animation. In the past, you had to either use the CPU do animate the character, or you needed to use *dedicated* hardware functions to do this. Now, you can simply write some code in a shader that can do it. That code would be moderately complex, but still much much faster than the CPU.
I'll use water as another example. For really
good looking water, you want to have two special textures - that you render at runtime - a reflection, and a refraction texture. Basically a texture with what is above and below the water. In that screenshot, the top left shows these two textures.
Now, in the past, those textures had to be mapped onto the water geometry. This was hard, because you had to construct the previously mentioned matrix that effects texture lookups in such a way as to project that texture such that it lines up with the screen. So a vertex at 0.5,0.5 on screen gets a texture coordinate of 0.5,0.5.
Now, you can do that easily in a vertex shader, as you already have all that data... textureCoordinate = position;
Furthermore, the reflection looks dull if it is static. It needs ripples, etc. In the past, faking this was crazy hard - Unless you had dedicated hardware features like EMBM or whatnot.
With a shader it is just maths, so when you lookup your texture, just offset the lookup with a 'ripple value'. Eg tex2D(texUnit0, textureCoordinate.xy + ripple.xy);
How do you generate that ripple value? Thats up to you entirely. Use another texture, use sin/cos, use vertex values, whatever. It's just maths now.
Want to make the reflection stronger at grazing angles? - more reflection in the distance? bigger ripples in deeper water?? Maths!
Shaders get very, very complex when crafting the effects seen in games. If you can think of something (in mathematical terms), then you can do it with shaders, since they are just maths.
Or even something very simple, say you wanted something to be twice as bright when the mouse hovered over it... Well, have a different shader - just with
* 2.0;
doubling the result. Even such a simple example would actually be really hard without shaders.
So basically shaders give you more control over how the scene looks and is constructed.