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:
Daniel Gibson 2021-06-15 03:24:36 +02:00
parent a8ef0f67fe
commit dc42bdc873
7 changed files with 184 additions and 2 deletions

View file

@ -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();
}
}
}
/*

View file

@ -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;

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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() {};