Forum

Gamma/Constrast

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Gamma/Constrast

Postby Baker » Wed Jul 14, 2010 6:52 pm

Just a look at the different methods of doing gamma, contrast and view blends. I'm having a minor but annoying issue so if I document the process I can also fix it :D But if I don't, I'll continue missing something that is eluding me.

My builds have 5 gamma/contrast methods:

1. WinQuake apply the gamma/contrast to the global palette method
2. GLQuake apply the gamma/contrast to the texture before upload method
3. D3DQuake's d3dSetGammaRamp method
4. The hardware gamma method
5. The render a brightness poly over everything method

Gamma

Building the gamma table from WinQuake. It later gets applied to the palette.

Code: Select all
void BuildGammaTable (float g) // g = gamma
{
   int      i, inf;
   
   if (g == 1.0)
   {
      for (i=0 ; i<256 ; i++)
         gammatable[i] = i;
      return;
   }
   
   for (i=0 ; i<256 ; i++)
   {
      inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;
      if (inf < 0)
         inf = 0;
      if (inf > 255)
         inf = 255;
      gammatable[i] = inf;
   }
}


Enter Contrast

The gamma/contrast table from JoeQuake:

Code: Select all
static void BuildGammaTable2 (float g, float c)
{
   int   i, inf;

   g = bound(0.3, g, 3);
   c = bound(1, c, 3);

   if (g == 1 && c == 1)
   {
      for (i=0 ; i<256 ; i++)
         gammatable[i] = i;
      return;
   }

   for (i=0 ; i<256 ; i++)
   {
      inf = 255 * pow((i + 0.5) / 255.5 * c, g) + 0.5;
      gammatable[i] = bound(0, inf, 255);
   }
}


I'll be stepping through where each of these are done and why as far as the application of the gamma and contrast.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby metlslime » Wed Jul 14, 2010 8:17 pm

don't forget idgamma, which causes people to complain that overbright lighting is bad :)

Note: I think these two are the same:

3. D3DQuake's d3dSetGammaRamp method
4. The hardware gamma method

The only difference being which API you use; d3d or wgl. But they both affect the video card the same way (lookup table in the DAC i believe.)
metlslime
 
Posts: 316
Joined: Tue Feb 05, 2008 11:03 pm

Postby Spike » Wed Jul 14, 2010 8:22 pm

yeah, setgammaramp is hardware gamma, but the d3d one has less down sides
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby Baker » Wed Jul 14, 2010 8:29 pm

View Blends

Combined with the gamma [and contrast] the view blends also affect the palette flashes in WinQuake directly:

Code: Select all
typedef struct
{
   int      destcolor[3];
   int      percent;      // 0-256
} cshift_t;

#define   CSHIFT_CONTENTS   0
#define   CSHIFT_DAMAGE   1
#define   CSHIFT_BONUS   2
#define   CSHIFT_POWERUP   3
#define   NUM_CSHIFTS      4


There are 4 chsifts -- I'm guessing the C stands for contrast but it could stand for client. Or maybe contents? Maybe something else (?)

Contents: Empty, Water, Lava, Slime, "Void". Void = no clipping outside the map
Damage: Obvious.
Bonus: Item pickups. "bf"
PowerUP: Quad, Pent, Ring, Suit

The cshift constants ... red, green, blue, intensity percent.

Code: Select all
cshift_t   cshift_empty = { {130,80,50}, 0 };
cshift_t   cshift_water = { {130,80,50}, 128 };
cshift_t   cshift_slime = { {0,25,5}, 150 };
cshift_t   cshift_lava = { {255,80,0}, 150 };


Note: Kurok added an extra one for blue when you go underwater.

Code: Select all
cshift_t   cshift_kwater = { {64,64,128}, 128 }; // Blue water for Kurok


The lifecycle of the CSHIFT begins in R_Render_View_ right after drawing particles.

Code: Select all
void V_SetContentsColor (int contents)
{
   switch (contents)
   {
   case CONTENTS_EMPTY:
   case CONTENTS_SOLID:
      cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
      break;
   case CONTENTS_LAVA:
      cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
      break;
   case CONTENTS_SLIME:
      cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
      break;
   default:
      cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
   }
}


Bonus Flash is actually a command sent over the server ...

Code: Select all
void V_BonusFlash_f (void)
{
   cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
   cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
   cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
   cl.cshifts[CSHIFT_BONUS].percent = 50;
}


It is received in CL_ReadFromServer --> CL_ParseServerMessage and added to the command buffer. So it is probably actually processed 1 frame later.

The damage flash is processed the moment it is received from CL_ParseServerMessage

Code: Select all
      case svc_damage:
         V_ParseDamage ();
         break;


Power up shift is calculated in V_CalcPowerupCShift, which is called by V_UpdatePalette

Code: Select all
/*
=============
V_CalcPowerupCshift
=============
*/
void V_CalcPowerupCshift (void)
{
   if (cl.items & IT_QUAD)
   {
      cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
      cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
      cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
      cl.cshifts[CSHIFT_POWERUP].percent = 30;
   }
   else if (cl.items & IT_SUIT)
   {
      cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
      cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
      cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
      cl.cshifts[CSHIFT_POWERUP].percent = 20;
   }
   else if (cl.items & IT_INVISIBILITY)
   {
      cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
      cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
      cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
      cl.cshifts[CSHIFT_POWERUP].percent = 100;
   }
   else if (cl.items & IT_INVULNERABILITY)
   {
      cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
      cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
      cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
      cl.cshifts[CSHIFT_POWERUP].percent = 30;
   }
   else
      cl.cshifts[CSHIFT_POWERUP].percent = 0;
}


Funny note, apparently the content blend was meant to be cleared in WinQuake if you pulled up the console ...

Code: Select all
/*
===============
SCR_BringDownConsole

Brings the console down and fades the palettes back to normal
================
*/
void SCR_BringDownConsole (void)
{
   int      i;
   
   scr_centertime_off = 0;
   
   for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
      SCR_UpdateScreen ();

   cl.cshifts[0].percent = 0;      // no area contents palette on next frame
   VID_SetPalette (host_basepal);
}


It doesn't work.

Probably because SCR_BringDownConsole is never called anywhere in the source and is commented out in cl_main.c :D
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby mh » Wed Jul 14, 2010 8:55 pm

"Colour" (or "Color" for those who didn't flunk math and married the prom queen) I would guess.

I don't bother with the D3D gamma ramp and just use the GDI functions instead. My guess is that the D3D version is just a wrapper around the GDI functions anyway (which may or may not work in windowed modes depending on which page of the documentation you read). It does have the ability to do calibration but I've never felt the need for it.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby Baker » Wed Jul 14, 2010 9:03 pm

metlslime wrote:3. D3DQuake's d3dSetGammaRamp method
4. The hardware gamma method


d3dquake's D3DSetGamma only affects the window itself and not the desktop. I noticed that FTEQW 2770 using the D3D render actually uses hardware and contrast, which looks much better.

mh wrote:"Colour"


Baker -1
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby Spike » Wed Jul 14, 2010 9:29 pm

why d3d's setgammaramps function is better than the windows api:
D3DSGR_NO_CALIBRATION
no stall, no bounds checks, what you send actually goes.

d3d8 only works if its fullscreen. d3d9 works even windowed (affecting the whole screen).

its probably worth creating a d3d9 context just to get ramps working without stalls, even if all rendering is done with opengl.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby mh » Wed Jul 14, 2010 9:35 pm

Spike wrote:why d3d's setgammaramps function is better than the windows api:
D3DSGR_NO_CALIBRATION
no stall, no bounds checks, what you send actually goes.

d3d8 only works if its fullscreen. d3d9 works even windowed (affecting the whole screen).

its probably worth creating a d3d9 context just to get ramps working without stalls, even if all rendering is done with opengl.

I think I now have the need to use D3D's SGR. :D

(Although if my theory is correct there should be an API call to disable the crap too).
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest