In my experience, some features do not work without shaders.... or only a small number of combinations. Shaders are (un)fortunately the only way these days.. because: ATI & Nvidia want big sales => the only market to give them great figures is the games industry => almost all game engines use shaders for special effects...
pixel (fragment) shaders are not difficult. All the information you need is here:
http://oss.sgi.com/projects/ogl-sample/registry/ARB/fragment_program.txtthere are different methods to program shaders (CG, GLSL, etc). My preferred one is still to write assembler (as described in the document linked above). Shaders are just small programs that get executed per pixel (fragment) after the rasterization (once all 3d calculation has been done and 2d projection performed).
To create a shader program, you just need a text editor (or you can embed you shader code in your prod). It's just a text string written in assembler.
To use a shader, you need to create an ID for it (just like for a texture). Use glGetProgramsARB() for this.
Then to activate a shader, you need to bind it (again just like for a texture). Use glBindProgramARB() for this.
In order to have your shader assembled on the graphics card's GPU, you need to assemble it using glProgramStringARB(). Obviously you need to have a shader ID generated before and the ID bound (active shader, see above)
Once the program is properly assembled (no errors) it can be bound when needed and whatever drawing command is performed, the active shader shader will be used for it.
If your shader uses one or more textures, then you need to bind your textures in the right texture slots. Use glActiveTextureARB() for this. Moderns graphics cards support up to 16 textures at the same time in a shader (although 32 will be possible quite soon).
In your shader program, you can use the TEX mnemonic to sample the texture in the right texture slot. Beware that the sampling method used by TEX depends on the texture format. if your texture is a power of 2 texture, then the sampling method has to be 2D, for rectangular textures, it has to be RECT (see documentation). If you mix this up, results are unpredictable (can work on some graphics card, or not work at all).
The GPU processes 4D vectors (x,y,z,w or r,g,b,a) entitities. Some instructions work on scalars, but most of them work on vectors, again see documentation for this.
Here is an example of a simple shader:
"!!ARBfp1.0\n"
"TEMP original_color;\n"
"# sample texture in slot 0, at fragemnt texture coordinates using RECT sampling \n"
"TEX original_color,fragment.texcoord,texture[0],RECT;\n"
/* final color */
"MOV result.color,original_color;\n"
/* end of shader */
"END\n\0"