Gamma 校正

     Gamma校正是全屏的, 现在的显卡都支持, 同样, Gamma校正可以实现一些特殊效果,各种图像淡入、淡出效果渐变等等。
   
DirectX Graphics中, 色彩到屏幕输出通过Gamma梯度映射校正, 如图12.2,实际不完全这样,参看下面的英文内容,一般是个抛物线.这里讲的只相当于亮度调整而已。

图12.2

    图中梯度映射校正最大为65535, 表示梯度使用WORD表示. 每种原色都有各自的梯度映射校正, 默认的三原色梯度映射校正都是相同的梯度斜率1, 例如对于#0080ff进行校正, 如图12.3

图12.3

0   [梯度映射校正]--> 0     [梯度到原色] --> 0   [最后输出颜色的RED分量]
128 [梯度映射校正]--> 32896 [梯度到原色] --> 128 [最后输出颜色的GREEN分量]
255 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最后输出颜色的BLUE分量]
现在我们改变GREEN分量的梯度, 使其斜率为2, 如图12.4, 则有

图12.4

0   [梯度映射校正]--> 0     [梯度到原色] --> 0   [最后输出颜色的RED分量]
128 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最后输出颜色的GREEN分量]
255 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最后输出颜色的BLUE分量]
使用这种Gamma校正, 屏幕会明显偏向于绿色.
DirectX Graphics, RED, GREEN, BLUE梯度通过D3DGAMMARAMP结构定义, 
typedef struct _D3DGAMMARAMP
{ 
    WORD                red [256]; 
    WORD                green[256]; 
    WORD                blue [256];
} D3DGAMMARAMP;

然后通过IDirect3DDevice9的函数SetGammaRamp设置,
void SetGammaRamp(UINT iSwapChain, DWORD Flags, CONST D3DGAMMARAMP * pRamp);
void GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP * pRamp);
也可以使用WIN32函数:
SetDeviceGammaRamp 设置gamma 和 GetDeviceGammaRamp 读取gamma(如果返回false则不支持gamma)
交换链 iSwapChain 一般选0,Flags 为:D3DSGR_NO_CALIBRATION 和 D3DSGR_CALIBRATE
控制应用gamma坡道之前是否对其进行校正,校正保证显示更加连续,但处理速度慢。通常仅设置gamma一次,这样不需要校正。如果想不断调整gamma制作动画效果可以使用坡道校正。
这里注意有些显卡使用WORD中的高位字节, 有些使用低位字节, 所以最好两个字节设置相同的数值, 如0xCOCO.
Directx 只支持对整个屏幕的Gamma 校正
SetGammaRamp will set the gamma ramp only on a full-screen mode application
Gamma 值范围 0.6 - 3.0
可用Gamma 实现淡入淡出效果:让所有数据由0渐变到索引值即可。
检测硬件是否支持Gamma校正:
device->GetDeviceCaps( &Caps );
 if( !(Caps.Caps2 & D3DCAPS2_FULLSCREENGAMMA) )
 {
       //不支持全屏Gamma校正
 }
if( !(Caps.Caps2 & D3DCAPS2_CANCALIBRATEGAMMA) )   
 {
       //不支持应用Gamma坡道之前进行校正效果,SetGammaRamp() 不能使用 D3DSGR_CALIBRATE 标志
 }

//quake3 中的调整gamma 方法。对应配置参数 r_gamma 调整 gamma, overbrightbits 调整亮度,r_intensity 调整对比度

 D3DGAMMARAMP GammaRamp; 
 float r_gamma = 1.6f; //gamma值,范围0.6-3.0
 static WORD s_gammatable[256];
 int  inf;
 int  shift = 0;   //亮度校正。范围视色深而定24位色及以上最大为2,16位最大为1 

 int i = 0;
 for ( i = 0; i < 256; i++ ) {
  if ( r_gamma == 1 ) {
   inf = i;
  } else {
   // gamma 校正算法,正常显示器gamma情况如下面的 fig1 图所示,要调节gamma 就要取 1/gamma 做指数,
   //得到 fig2 图示曲线, 从而叠加获得剃度直线
   inf = 255 * pow ( i/255.0f, 1.0f /r_gamma ) + 0.5f;   
  }
  inf <<= shift;   //overbright,对gamma剃度校正。感觉有了gamma校正可以不用这个.类似于gamma校正中的亮度校正
  if (inf < 0) {
   inf = 0;
  }
  if (inf > 255) {
   inf = 255;
  }
  s_gammatable[i] = inf<<8 | inf;     //使 word 的高位字节和低位字节相同
 }

 for ( i = 1 ; i < 256 ; i++ )
 {
  if ( s_gammatable[i] < s_gammatable[i-1] )
  {
   s_gammatable[i] = s_gammatable[i-1];
  }
 }

 for(i=0;i<256;++i) {
  GammaRamp.red[i] = s_gammatable[i];
  GammaRamp.green[i] = s_gammatable[i];
  GammaRamp.blue[i] = s_gammatable[i];
 }

 // 下面这种相对不够细腻,只相当于 overbright
 //int Overbrighten = 2;
 //int val;

 //for(i=0;i<256;++i) {
 // val = 256 * min( 255, i*Overbrighten );
 // GammaRamp.red[i] = val;
 // GammaRamp.green[i] = val;
 // GammaRamp.blue[i] = val;
 //}


 pd3dDevice->SetGammaRamp( 0, D3DSGR_NO_CALIBRATION, &GammaRamp );
 Sleep( 200 ); //fix occasional gamma problem (Gamma won't change when told to)
 pd3dDevice->SetGammaRamp( 0, D3DSGR_NO_CALIBRATION, &GammaRamp );

还有对比度:需要调整的是载入的图片的象素值。
每次退出要恢复成 gamma 线性值:即
    for (int g = 0; g < 255; g++ )
    {
     s_oldHardwareGamma[0][g] = g << 8;
     s_oldHardwareGamma[1][g] = g << 8;
     s_oldHardwareGamma[2][g] = g << 8;
    }
然后设置 gamma,因为程序可能非正常退出如:崩溃,或者调试退出.恢复gamma函数没有调用,几次之后 开始读取得gamma变成了上次非正常退出后调整过的值.所以恢复成线性值才能恢复到最原始的状态

I'm sure you've all seen gamma correction before, but you might not know exactly how it works or why it is necessary. In this article, I'll try to explain both, as well as provide practical information on how to implement gamma correction in your apps.

Intuitively, you might think of gamma correction as some sort of image brightness adjustment. That's not entirely wrong, but to understand why gamma correction is necessary, you need to look a little further. More precisely, you'll need to look inside your computer's display.

Chances are that your monitor is based on a cathode ray tube (CRT). A CRT uses an electron beam to make the phosphor coating inside the tube emit light. The brightness of a pixel is changed by varying the voltage of the electron beam as it touches that pixel. Unfortunately, the relation between CRT voltage and pixel brightness isn't linear. It looks something more like this:

 

This non-linearity effect is called gamma. Unfortunately, it causes the image displayed on your computer screen to be different from the image that is actually in memory. What you really want to see is a nice linear mapping between voltage and brightness. In other words: the graph above should be a straight line.

That's why you should perform gamma correction. Gamma correction is performed by remapping the colors in the image as such: 

The gamma-corrected color ramp can be calculated by raising the original color values to some power:

The exponent to use is the so-called "gamma value", which you can see in paint programs that support gamma correction. The gamma value should be user-configurable, of course.

You've probably already noticed: by using the gamma-corrected color ramp, you can cancel out the gamma effect of your monitor, resulting in a correct display of the image:

This leaves one thing to be discussed: how to implement gamma correction. If you're just working with bitmap images, you can perform it manually for each pixel. This being an OpenGL site and all, I suspect that this isn't really what you want to do. Luckily, you can ask your graphics card to perform the correction for you (in real-time, of course). To this effect, you can use the GetDeviceGammaRamp() and SetDeviceGammaRamp() API calls. I've written a small OpenGL demo that illustrates the use of these functions. Download it here.

That's all there's to it! Gamma correction is easy enough to understand and implement - and especially for games with dark environments, it can make a huge difference. Gamma correction will allow users to adjust the image brightness to suit their monitor, allowing them to see your game or application as you intended them to see it.

用MFC 对话框写了一个调整 Gamma 的程序,没有使用directx 函数,替代的是操作系统的函数。下载地址
http://www.91files.com/?2LKXQ8ZGOX9C8MEKN34X
参考网址:
http://www.cameraunion.net/forum/showthread.php?threadid=179245
http://bbs.photoshopcn.com/archiver/tid-376356.html
http://www.delphi3d.net/articles/viewarticle.php?article=gamma.htm