mirror of
https://github.com/dhewm/dhewm3.git
synced 2025-03-21 02:01:03 +00:00
Do gamma correction (r_gamma, r_brightness) in shaders
by injecting code to do it into fragment shaders. Can be disabled with r_gammaInShaders 0 refs #385
This commit is contained in:
parent
a8ef0f67fe
commit
dc42bdc873
7 changed files with 184 additions and 2 deletions
|
@ -246,6 +246,20 @@ static void R_CheckCvars( void ) {
|
|||
r_brightness.ClearModified();
|
||||
R_SetColorMappings();
|
||||
}
|
||||
|
||||
if ( r_gammaInShader.IsModified() ) {
|
||||
r_gammaInShader.ClearModified();
|
||||
// reload shaders so they either add or remove the code for setting gamma/brightness in shader
|
||||
R_ReloadARBPrograms_f( idCmdArgs() );
|
||||
|
||||
if ( r_gammaInShader.GetBool() ) {
|
||||
common->Printf( "Will apply r_gamma and r_brightness in shaders\n" );
|
||||
GLimp_ResetGamma(); // reset hardware gamma
|
||||
} else {
|
||||
common->Printf( "Will apply r_gamma and r_brightness in hardware (possibly on all screens)\n" );
|
||||
R_SetColorMappings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -91,6 +91,7 @@ idCVar r_swapInterval( "r_swapInterval", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVA
|
|||
|
||||
idCVar r_gamma( "r_gamma", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_FLOAT, "changes gamma tables", 0.5f, 3.0f );
|
||||
idCVar r_brightness( "r_brightness", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_FLOAT, "changes gamma tables", 0.5f, 2.0f );
|
||||
idCVar r_gammaInShader( "r_gammaInShader", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Set gamma and brightness in shaders instead using hardware gamma" );
|
||||
|
||||
idCVar r_renderer( "r_renderer", "best", CVAR_RENDERER | CVAR_ARCHIVE, "hardware specific renderer path to use", r_rendererArgs, idCmdSystem::ArgCompletion_String<r_rendererArgs> );
|
||||
|
||||
|
@ -1757,6 +1758,12 @@ R_SetColorMappings
|
|||
===============
|
||||
*/
|
||||
void R_SetColorMappings( void ) {
|
||||
|
||||
if ( r_gammaInShader.GetBool() ) {
|
||||
// nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
int i, j;
|
||||
float g, b;
|
||||
int inf;
|
||||
|
|
|
@ -98,6 +98,15 @@ void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
|
|||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
|
||||
|
||||
// DG: brightness and gamma in shader as program.env[4]
|
||||
if ( r_gammaInShader.GetBool() ) {
|
||||
// program.env[4].xyz are all r_brightness, program.env[4].w is 1.0/r_gamma
|
||||
float parm[4];
|
||||
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
|
||||
parm[3] = 1.0/r_gamma.GetFloat(); // 1.0/gamma so the shader doesn't have to do this calculation
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
|
||||
}
|
||||
|
||||
// set the textures
|
||||
|
||||
// texture 1 will be the per-surface bump map
|
||||
|
@ -344,6 +353,31 @@ static progDef_t progs[MAX_GLPROGS] = {
|
|||
R_LoadARBProgram
|
||||
=================
|
||||
*/
|
||||
|
||||
static char* findLineThatStartsWith( char* text, const char* findMe ) {
|
||||
char* res = strstr( text, findMe );
|
||||
while ( res != NULL ) {
|
||||
// skip whitespace before match, if any
|
||||
char* cur = res;
|
||||
if ( cur > text ) {
|
||||
--cur;
|
||||
}
|
||||
while ( cur > text && ( *cur == ' ' || *cur == '\t' ) ) {
|
||||
--cur;
|
||||
}
|
||||
// now we should be at a newline (or at the beginning)
|
||||
if ( cur == text ) {
|
||||
return cur;
|
||||
}
|
||||
if ( *cur == '\n' || *cur == '\r' ) {
|
||||
return cur+1;
|
||||
}
|
||||
// otherwise maybe we're in commented out text or whatever, search on
|
||||
res = strstr( res+1, findMe );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void R_LoadARBProgram( int progIndex ) {
|
||||
int ofs;
|
||||
int err;
|
||||
|
@ -409,6 +443,69 @@ void R_LoadARBProgram( int progIndex ) {
|
|||
}
|
||||
end[3] = 0;
|
||||
|
||||
// DG: hack gamma correction into shader
|
||||
if ( r_gammaInShader.GetBool() && progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
|
||||
|
||||
// note that strlen("dhewm3tmpres") == strlen("result.color")
|
||||
const char* tmpres = "TEMP dhewm3tmpres; # injected by dhewm3 for gamma correction\n";
|
||||
|
||||
// Note: program.env[4].xyz = r_brightness; program.env[4].w = 1.0/r_gamma
|
||||
// outColor.rgb = pow(dhewm3tmpres.rgb*r_brightness, vec3(1.0/r_gamma))
|
||||
// outColor.a = dhewm3tmpres.a;
|
||||
const char* extraLines =
|
||||
"# gamma correction in shader, injected by dhewm3 \n"
|
||||
"MUL dhewm3tmpres.xyz, program.env[4], dhewm3tmpres;\n" // first multiply with brightness
|
||||
"POW result.color.x, dhewm3tmpres.x, program.env[4].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
|
||||
"POW result.color.y, dhewm3tmpres.y, program.env[4].w;\n" // (apparently POW only supports scalars, not whole vectors)
|
||||
"POW result.color.z, dhewm3tmpres.z, program.env[4].w;\n"
|
||||
"MOV result.color.w, dhewm3tmpres.w;\n" // alpha remains unmodified
|
||||
"\nEND\n\n"; // we add this block right at the end, replacing the original "END" string
|
||||
|
||||
int fullLen = strlen( start ) + strlen( tmpres ) + strlen( extraLines );
|
||||
char* outStr = (char*)_alloca( fullLen + 1 );
|
||||
|
||||
// add tmpres right after OPTION line (if any)
|
||||
char* insertPos = findLineThatStartsWith( start, "OPTION" );
|
||||
if ( insertPos == NULL ) {
|
||||
// no OPTION? then just put it after the first line (usually sth like "!!ARBfp1.0\n")
|
||||
insertPos = start;
|
||||
}
|
||||
// but we want the position *after* that line
|
||||
while( *insertPos != '\0' && *insertPos != '\n' && *insertPos != '\r' ) {
|
||||
++insertPos;
|
||||
}
|
||||
// skip the newline character(s) as well
|
||||
while( *insertPos == '\n' || *insertPos == '\r' ) {
|
||||
++insertPos;
|
||||
}
|
||||
|
||||
// copy text up to insertPos
|
||||
int curLen = insertPos-start;
|
||||
memcpy( outStr, start, curLen );
|
||||
// copy tmpres ("TEMP dhewm3tmpres; # ..")
|
||||
memcpy( outStr+curLen, tmpres, strlen( tmpres ) );
|
||||
curLen += strlen( tmpres );
|
||||
// copy remaining original shader up to (excluding) "END"
|
||||
int remLen = end - insertPos;
|
||||
memcpy( outStr+curLen, insertPos, remLen );
|
||||
curLen += remLen;
|
||||
|
||||
outStr[curLen] = '\0'; // make sure it's NULL-terminated so normal string functions work
|
||||
|
||||
// replace all existing occurrences of "result.color" with "dhewm3tmpres"
|
||||
for( char* resCol = strstr( outStr, "result.color" );
|
||||
resCol != NULL; resCol = strstr( resCol+13, "result.color" ) ) {
|
||||
memcpy( resCol, "dhewm3tmpres", 12 ); // both strings have the same length.
|
||||
}
|
||||
|
||||
assert( curLen + strlen( extraLines ) <= fullLen );
|
||||
|
||||
// now add extraLines that calculate and set a gamma-corrected result.color
|
||||
// strcat() should be safe because fullLen was calculated taking all parts into account
|
||||
strcat( outStr, extraLines );
|
||||
start = outStr;
|
||||
}
|
||||
|
||||
qglBindProgramARB( progs[progIndex].target, progs[progIndex].ident );
|
||||
qglGetError();
|
||||
|
||||
|
|
|
@ -577,7 +577,7 @@ RB_SetProgramEnvironment
|
|||
Sets variables that can be used by all vertex programs
|
||||
==================
|
||||
*/
|
||||
void RB_SetProgramEnvironment( void ) {
|
||||
void RB_SetProgramEnvironment( bool isPostProcess ) {
|
||||
float parm[4];
|
||||
int pot;
|
||||
|
||||
|
@ -632,6 +632,20 @@ void RB_SetProgramEnvironment( void ) {
|
|||
parm[3] = 1;
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, parm );
|
||||
|
||||
// DG: brightness and gamma in shader as program.env[4]
|
||||
if ( r_gammaInShader.GetBool() ) {
|
||||
// program.env[4].xyz are all r_brightness, program.env[4].w is 1.0/r_gamma
|
||||
if ( !isPostProcess ) {
|
||||
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
|
||||
parm[3] = 1.0/r_gamma.GetFloat(); // 1.0/gamma so the shader doesn't have to do this calculation
|
||||
} else {
|
||||
// don't apply gamma/brightness in postprocess passes to avoid applying them twice
|
||||
// (setting them to 1.0 makes them no-ops)
|
||||
parm[0] = parm[1] = parm[2] = parm[3] = 1.0f;
|
||||
}
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
|
||||
}
|
||||
|
||||
//
|
||||
// set eye position in global space
|
||||
//
|
||||
|
@ -978,12 +992,15 @@ int RB_STD_DrawShaderPasses( drawSurf_t **drawSurfs, int numDrawSurfs ) {
|
|||
return numDrawSurfs;
|
||||
}
|
||||
|
||||
bool isPostProcess = false;
|
||||
|
||||
// if we are about to draw the first surface that needs
|
||||
// the rendering in a texture, copy it over
|
||||
if ( drawSurfs[0]->material->GetSort() >= SS_POST_PROCESS ) {
|
||||
if ( r_skipPostProcess.GetBool() ) {
|
||||
return 0;
|
||||
}
|
||||
isPostProcess = true;
|
||||
|
||||
// only dump if in a 3d view
|
||||
if ( backEnd.viewDef->viewEntitys && tr.backEndRenderer == BE_ARB2 ) {
|
||||
|
@ -1000,7 +1017,7 @@ int RB_STD_DrawShaderPasses( drawSurf_t **drawSurfs, int numDrawSurfs ) {
|
|||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
RB_SetProgramEnvironment();
|
||||
RB_SetProgramEnvironment( isPostProcess );
|
||||
|
||||
// we don't use RB_RenderDrawSurfListWithFunction()
|
||||
// because we want to defer the matrix load because many
|
||||
|
|
|
@ -837,6 +837,7 @@ extern idCVar r_flareSize; // scale the flare deforms from the material def
|
|||
|
||||
extern idCVar r_gamma; // changes gamma tables
|
||||
extern idCVar r_brightness; // changes gamma tables
|
||||
extern idCVar r_gammaInShader; // set gamma+brightness in shader instead of modifying system gamma tables
|
||||
|
||||
extern idCVar r_renderer; // arb2, etc
|
||||
|
||||
|
@ -1092,6 +1093,8 @@ void GLimp_SetGamma( unsigned short red[256],
|
|||
// These are now taken as 16 bit values, so we can take full advantage
|
||||
// of dacs with >8 bits of precision
|
||||
|
||||
void GLimp_ResetGamma();
|
||||
// resets the gamma to what it was at startup
|
||||
|
||||
// Returns false if the system only has a single processor
|
||||
|
||||
|
|
|
@ -540,6 +540,12 @@ void GLimp_SwapBuffers() {
|
|||
#endif
|
||||
}
|
||||
|
||||
static bool gammaOrigError = false;
|
||||
static bool gammaOrigSet = false;
|
||||
static unsigned short gammaOrigRed[256];
|
||||
static unsigned short gammaOrigGreen[256];
|
||||
static unsigned short gammaOrigBlue[256];
|
||||
|
||||
/*
|
||||
=================
|
||||
GLimp_SetGamma
|
||||
|
@ -551,6 +557,19 @@ void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned
|
|||
return;
|
||||
}
|
||||
|
||||
if ( !gammaOrigSet ) {
|
||||
gammaOrigSet = true;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if ( SDL_GetWindowGammaRamp( window, gammaOrigRed, gammaOrigGreen, gammaOrigBlue ) == -1 ) {
|
||||
#else
|
||||
if ( SDL_GetGammaRamp( gammaOrigRed, gammaOrigGreen, gammaOrigBlue ) == -1 ) {
|
||||
#endif
|
||||
gammaOrigError = true;
|
||||
common->Warning( "Failed to get Gamma Ramp: %s\n", SDL_GetError() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (SDL_SetWindowGammaRamp(window, red, green, blue))
|
||||
#else
|
||||
|
@ -559,6 +578,30 @@ void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned
|
|||
common->Warning("Couldn't set gamma ramp: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
GLimp_ResetGamma
|
||||
|
||||
Restore original system gamma setting
|
||||
=================
|
||||
*/
|
||||
void GLimp_ResetGamma() {
|
||||
if( gammaOrigError ) {
|
||||
common->Warning( "Can't reset hardware gamma because getting the Gamma Ramp at startup failed!\n" );
|
||||
common->Warning( "You might have to restart the game for gamma/brightness in shaders to work properly.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( gammaOrigSet ) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_SetWindowGammaRamp( window, gammaOrigRed, gammaOrigGreen, gammaOrigBlue );
|
||||
#else
|
||||
SDL_SetGammaRamp( gammaOrigRed, gammaOrigGreen, gammaOrigBlue );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
GLimp_ActivateContext
|
||||
|
|
|
@ -389,6 +389,7 @@ GLExtension_t GLimp_ExtensionPointer( const char *a) { return StubFunction; };
|
|||
|
||||
bool GLimp_Init(glimpParms_t a) {return true;};
|
||||
void GLimp_SetGamma(unsigned short*a, unsigned short*b, unsigned short*c) {};
|
||||
void GLimp_ResetGamma() {}
|
||||
bool GLimp_SetScreenParms(glimpParms_t parms) { return true; };
|
||||
void GLimp_Shutdown() {};
|
||||
void GLimp_SwapBuffers() {};
|
||||
|
|
Loading…
Reference in a new issue