renderergl2: reimplement greyscale as backend post after all 2D+3D draws

Fixes #654 - HUD/UI previously bypassed greyscale.
r_greyscale now supports partial desaturation [0-1].
Removed unused legacy logic from tr_shade.c
This commit is contained in:
Lojjik Braughler 2025-11-01 11:54:29 +00:00 committed by Tim Angus
parent 7bd2c17f9d
commit 453705553b
9 changed files with 166 additions and 111 deletions

View File

@ -0,0 +1,15 @@
uniform sampler2D u_TextureMap;
uniform float u_Greyscale;
varying vec2 var_TexCoords;
// From Rec. 709-1 4.2 "Derivation of luminance signal"
const vec3 LUMA = vec3(0.2125, 0.7154, 0.0721);
void main() {
vec4 color = texture2D(u_TextureMap, var_TexCoords);
if (u_Greyscale > 0.0) {
float y = dot(color.rgb, LUMA);
color.rgb = mix(color.rgb, vec3(y), clamp(u_Greyscale, 0.0, 1.0));
}
gl_FragColor = color;
}

View File

@ -0,0 +1,11 @@
attribute vec4 attr_Position;
attribute vec2 attr_TexCoord0;
varying vec2 var_TexCoords;
void main() {
vec2 clipXY = (attr_Position.xy * r_FBufScale) * 2.0 - 1.0;
clipXY.y = -clipXY.y;
gl_Position = vec4(clipXY, 0.0, 1.0);
var_TexCoords = attr_TexCoord0;
}

View File

@ -394,6 +394,9 @@ void RB_BeginDrawingView (void) {
// we will only draw a sun if there was sky rendered in this view
backEnd.skyRenderedThisView = qfalse;
// cache the clamped greyscale value
backEnd.greyscale = Com_Clamp(0.0f, 1.0f, r_greyscale->value);
// clip to the plane of the portal
if ( backEnd.viewParms.isPortal ) {
#if 0
@ -1357,6 +1360,84 @@ const void *RB_ClearDepth(const void *data)
return (const void *)(cmd + 1);
}
/*
=============
RB_DrawGreyscale
=============
*/
static void RB_DrawGreyscale(const FBO_t *src)
{
if (!src || !src->colorImage[0])
return;
FBO_Bind(NULL);
qglViewport(0, 0, glConfig.vidWidth, glConfig.vidHeight);
qglScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
GL_State(GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO);
GLSL_BindProgram(&tr.greyscaleShader);
GLSL_SetUniformInt(&tr.greyscaleShader, UNIFORM_TEXTUREMAP, 0);
GLSL_SetUniformFloat(&tr.greyscaleShader, UNIFORM_GREYSCALE, backEnd.greyscale);
GL_BindToTMU(src->colorImage[0], 0);
vec4_t quadVerts[4] = {
{ 0.0f, 0.0f, 0.0f, 1.0f },
{ (float)glConfig.vidWidth, 0.0f, 0.0f, 1.0f },
{ (float)glConfig.vidWidth, (float)glConfig.vidHeight, 0.0f, 1.0f },
{ 0.0f, (float)glConfig.vidHeight, 0.0f, 1.0f },
};
vec2_t texCoords[4] = { {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f} };
RB_InstantQuad2(quadVerts, texCoords);
}
/*
=============
RB_PresentToScreen
=============
*/
static void RB_PresentToScreen(void)
{
if (!glRefConfig.framebufferObject)
return;
const FBO_t *src = NULL;
if (tr.renderFbo && tr.renderFbo->colorImage[0])
{
// texture-backed render FBO
src = tr.renderFbo;
}
else if (tr.msaaResolveFbo && r_hdr->integer)
{
// Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first
FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
src = tr.msaaResolveFbo;
}
if (src)
{
if (backEnd.greyscale > 0.0f)
{
RB_DrawGreyscale(src);
}
else
{
FBO_FastBlit(src, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
return;
}
if (tr.renderFbo)
{
FBO_FastBlit(tr.renderFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
}
/*
=============
@ -1397,19 +1478,7 @@ const void *RB_SwapBuffers( const void *data ) {
ri.Hunk_FreeTempMemory( stencilReadback );
}
if (glRefConfig.framebufferObject)
{
if (tr.msaaResolveFbo && r_hdr->integer)
{
// Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first
FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
FBO_FastBlit(tr.msaaResolveFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
else if (tr.renderFbo)
{
FBO_FastBlit(tr.renderFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
}
RB_PresentToScreen();
if ( !glState.finishCalled ) {
qglFinish();

View File

@ -592,7 +592,7 @@ void FBO_BlitFromTexture(struct image_s *src, vec4_t inSrcTexCorners, vec2_t inS
FBO_Bind(oldFbo);
}
void FBO_Blit(FBO_t *src, ivec4_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, ivec4_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend)
void FBO_Blit(const FBO_t *src, ivec4_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, ivec4_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend)
{
vec4_t srcTexCorners;
@ -617,7 +617,7 @@ void FBO_Blit(FBO_t *src, ivec4_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, ivec
FBO_BlitFromTexture(src->colorImage[0], srcTexCorners, srcTexScale, dst, dstBox, shaderProgram, color, blend | GLS_DEPTHTEST_DISABLE);
}
void FBO_FastBlit(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int buffers, int filter)
void FBO_FastBlit(const FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int buffers, int filter)
{
ivec4_t srcBoxFinal, dstBoxFinal;
GLuint srcFb, dstFb;

View File

@ -58,8 +58,8 @@ void FBO_Init(void);
void FBO_Shutdown(void);
void FBO_BlitFromTexture(struct image_s *src, vec4_t inSrcTexCorners, vec2_t inSrcTexScale, FBO_t *dst, ivec4_t inDstBox, struct shaderProgram_s *shaderProgram, vec4_t inColor, int blend);
void FBO_Blit(FBO_t *src, ivec4_t srcBox, vec2_t srcTexScale, FBO_t *dst, ivec4_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend);
void FBO_FastBlit(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int buffers, int filter);
void FBO_Blit(const FBO_t *src, ivec4_t srcBox, vec2_t srcTexScale, FBO_t *dst, ivec4_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend);
void FBO_FastBlit(const FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int buffers, int filter);
#endif

View File

@ -52,6 +52,8 @@ extern const char *fallbackShader_texturecolor_vp;
extern const char *fallbackShader_texturecolor_fp;
extern const char *fallbackShader_tonemap_vp;
extern const char *fallbackShader_tonemap_fp;
extern const char* fallbackShader_greyscale_vp;
extern const char* fallbackShader_greyscale_fp;
typedef struct uniformInfo_s
{
@ -156,6 +158,7 @@ static uniformInfo_t uniformsInfo[] =
{ "u_AlphaTest", GLSL_INT },
{ "u_BoneMatrix", GLSL_MAT16_BONEMATRIX },
{ "u_Greyscale", GLSL_FLOAT }
};
typedef enum
@ -1451,6 +1454,18 @@ void GLSL_InitGPUShaders(void)
}
if (!GLSL_InitGPUShader(&tr.greyscaleShader, "greyscale", attribs, qtrue, extradefines, qtrue,
fallbackShader_greyscale_vp, fallbackShader_greyscale_fp))
{
ri.Error(ERR_FATAL, "Unable to load greyscale shader");
}
GLSL_InitUniforms(&tr.greyscaleShader);
GLSL_SetUniformInt(&tr.greyscaleShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.greyscaleShader);
numEtcShaders++;
// GLSL 1.10+ or GL_OES_standard_derivatives extension are required for dFdx() and dFdy() GLSL functions
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|| glRefConfig.standardDerivatives)

View File

@ -1805,11 +1805,7 @@ static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picForm
}
else if(lightMap)
{
// GL_LUMINANCE is not valid for OpenGL 3.2 Core context and
// everything becomes solid black
if(0 && r_greyscale->integer)
internalFormat = GL_LUMINANCE;
else
internalFormat = GL_RGBA;
}
else
@ -1822,15 +1818,7 @@ static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picForm
// select proper internal format
if ( samples == 3 )
{
if(0 && r_greyscale->integer)
{
if(r_texturebits->integer == 16 || r_texturebits->integer == 32)
internalFormat = GL_LUMINANCE8;
else
internalFormat = GL_LUMINANCE;
}
else
{
if ( !forceNoCompression && (glRefConfig.textureCompression & TCR_BPTC) )
{
internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
@ -1856,17 +1844,7 @@ static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picForm
internalFormat = GL_RGB;
}
}
}
else if ( samples == 4 )
{
if(0 && r_greyscale->integer)
{
if(r_texturebits->integer == 16 || r_texturebits->integer == 32)
internalFormat = GL_LUMINANCE8_ALPHA8;
else
internalFormat = GL_LUMINANCE_ALPHA;
}
else
{
if ( !forceNoCompression && (glRefConfig.textureCompression & TCR_BPTC) )
{
@ -1890,7 +1868,6 @@ static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picForm
}
}
}
}
return internalFormat;
}
@ -2129,7 +2106,6 @@ Upload32
static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, image_t *image, qboolean scaled)
{
int i, c;
byte *scan;
imgType_t type = image->type;
imgFlags_t flags = image->flags;
@ -2142,30 +2118,9 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
if (rgba8 && !cubemap)
{
c = width*height;
scan = data;
if (type == IMGTYPE_COLORALPHA)
{
if( r_greyscale->integer )
{
for ( i = 0; i < c; i++ )
{
byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]);
scan[i*4] = luma;
scan[i*4 + 1] = luma;
scan[i*4 + 2] = luma;
}
}
else if( r_greyscale->value )
{
for ( i = 0; i < c; i++ )
{
float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]);
scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value);
scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value);
scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value);
}
}
// This corresponds to what the OpenGL1 renderer does.
if (!(flags & IMGFLAG_NOLIGHTSCALE) && (scaled || mipmap))

View File

@ -708,6 +708,8 @@ typedef enum
UNIFORM_BONEMATRIX,
UNIFORM_GREYSCALE,
UNIFORM_COUNT
} uniform_t;
@ -1494,6 +1496,7 @@ typedef struct {
FBO_t *last2DFBO;
qboolean colorMask[4];
qboolean depthFill;
float greyscale;
} backEndState_t;
/*
@ -1614,6 +1617,7 @@ typedef struct {
shaderProgram_t ssaoShader;
shaderProgram_t depthBlurShader[4];
shaderProgram_t testcubeShader;
shaderProgram_t greyscaleShader;
// -----------------------------------------

View File

@ -722,20 +722,6 @@ static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t
break;
}
// FIXME: find some way to implement this.
#if 0
// if in greyscale rendering mode turn all color values into greyscale.
if(r_greyscale->integer)
{
int scale;
for(i = 0; i < tess.numVertexes; i++)
{
scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3;
tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale;
}
}
#endif
}