Yes, in theory, you could use ARB_imaging to transform the framebuffer into grayscale. You’d use CopyPixels of the entire screen (or at least the region in question) with a color matrix, and with ReadBuffer and DrawBuffer both GL_BACK, before SwapBuffers. You could put the NTSC R, G, B scaling values in the appropriate matrix elements. You could also select the “degree” of grayscale that you wanted, if you want only a partial grayscale effect.
Don’t blame me if it’s really slow. (Well, actually, if there was anyone to blame, it’d be me, seeing as I implemented it, so you can blame me, and you are fully within your rights to blame me, but I refuse to accept your blame.)
A faster way to implement it might be to use CopyTexSubImage to copy that portion of the buffer into a texture, then to render using that texture, using NV_register_combiners to implement a 3x3 color matrix. It’s actually a pretty efficient way to use the combiners, since it takes advantage of a lot of per-pixel math!
A dot B computes texture dot Crr,Cgr,Cbr --> Spare0
C dot D computes texture dot Crg,Cgg,Cbg --> Spare1
A dot B computes texture dot Crb,Cgb,Cbb --> Spare0
CD computes Spare0 * 1,0,0 --> Texture0
EF computes 0,1,0 * Spare1
AB + (1-A)C + D computes 0,0,1Spare0 * 1,1,0EF + Texture0 = 0,1,0Spare0 + 0,1,0Spare1 + Texture0
You need 6 constant colors – three sets of matrix coefficients and three primary colors. Two can go in the constant0 and constant1 registers, two can go in the primary and secondary interpolated colors, one can go in the fog color, and the last can be put in a 1x1 texture and bound to the second texture unit. So it just BARELY fits.
Note that we can’t use register combiners to implement the color matrix internally because the combiners have a [-1,1] range limitation and insufficient precision (pixel path operations are generally assumed to be performed with floating point precision).