diff --git a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl index 126fc12bcc..68a1705348 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl +++ b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl @@ -53,7 +53,7 @@ void main() // st.s = vertexDistanceToCamera; st.t = 1.0; - vec4 color = texture2D(u_ColorMap, st); + vec4 color = vec4( vec3( 1.0 ), texture2D( u_ColorMap, st ).r ); outputColor = UnpackColor( u_Color ) * color; } diff --git a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl b/src/engine/renderer/glsl_source/fogQuake3_fp.glsl index ab31ca9fb3..7a9826c191 100644 --- a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl +++ b/src/engine/renderer/glsl_source/fogQuake3_fp.glsl @@ -36,7 +36,7 @@ void main() { #insert material_fp - vec4 color = texture2D(u_FogMap, var_TexCoords); + vec4 color = vec4( vec3( 1.0 ), texture2D( u_FogMap, var_TexCoords ).r ); color *= var_Color; diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index a97f7347b0..877aa434ab 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -739,12 +739,11 @@ static GLint GL_ToSRGB( GLint internalFormat, bool isSRGB ) { switch ( format ) { -#if 0 // Not used in the code base. /* EXT_texture_sRGB_R8 extension. See: https://github.com/KhronosGroup/OpenGL-Registry/blob/main/extensions/EXT/EXT_texture_sRGB_R8.txt */ case GL_RED: + ASSERT( glConfig2.textureSrgbR8Available ); return GL_SR8_EXT; -#endif case GL_RGB: return GL_SRGB; case GL_RGBA: diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index 5a4079173d..99bc9b6e99 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3954,6 +3954,8 @@ void R_LoadEntities( lump_t *l, std::string &externalEntities ) tr.convertColorFromSRGB = convertColorFromSRGB_cheap; } } + + tr.fogImage = tr.worldLinearizeTexture ? tr.fogImageLinear : tr.fogImageNaive; } /* diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index 6a669a0556..052143f47b 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -176,6 +176,7 @@ class ListImagesCmd : public Cmd::StaticCmd { GL_RGBA32UI, { "RGBA32UI", 16 } }, { GL_ALPHA16F_ARB, { "A16F", 2 } }, { GL_ALPHA32F_ARB, { "A32F", 4 } }, + { GL_RED, { "R8", 1 } }, { GL_R16F, { "R16F", 2 } }, { GL_R32F, { "R32F", 4 } }, { GL_LUMINANCE_ALPHA16F_ARB, { "LA16F", 4 } }, @@ -933,6 +934,18 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int format = GL_DEPTH_STENCIL; internalFormat = GL_DEPTH24_STENCIL8; } + else if ( image->bits & IF_RED ) + { + if( isSRGB && !glConfig2.textureSrgbR8Available ) + { + Log::Warn("red image '%s' cannot be loaded as sRGB", image->name ); + internalFormat = GL_RGBA8; + } + else + { + internalFormat = GL_RED; + } + } else if ( image->bits & ( IF_RGBA16F | IF_RGBA32F | IF_TWOCOMP16F | IF_TWOCOMP32F | IF_ONECOMP16F | IF_ONECOMP32F ) ) { if( !glConfig2.textureFloatAvailable ) { @@ -2405,26 +2418,23 @@ static void R_CreateFogImage() { // Fog image is always created because disabling fog is cheat. - int x, y; - byte *data, *ptr; - float d; - float borderColor[ 4 ]; - - constexpr int FOG_S = 256; - constexpr int FOG_T = 32; + constexpr size_t FOG_S = 256; + constexpr size_t FOG_T = 32; + constexpr size_t channels = 4; - ptr = data = (byte*) ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); + byte *data, *ptr; + ptr = data = (byte*) ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * channels ); // S is distance, T is depth - for ( y = 0; y < FOG_T; y++ ) + for ( size_t y = 0; y < FOG_T; y++ ) { - for ( x = 0; x < FOG_S; x++ ) + for ( size_t x = 0; x < FOG_S; x++ ) { - d = R_FogFactor( ( x + 0.5f ) / FOG_S, ( y + 0.5f ) / FOG_T ); + float d = R_FogFactor( ( x + 0.5f ) / FOG_S, ( y + 0.5f ) / FOG_T ); - ptr[ 0 ] = ptr[ 1 ] = ptr[ 2 ] = 255; - ptr[ 3 ] = 255 * d; - ptr += 4; + ptr[ 0 ] = 255 * d; + ptr[ 1 ] = ptr[ 2 ] = ptr[ 3 ] = 255; + ptr += channels; } } @@ -2432,17 +2442,32 @@ static void R_CreateFogImage() // the border color at the edges. OpenGL 1.2 has clamp-to-edge, which does // what we want. imageParams_t imageParams = {}; - imageParams.bits = IF_NOPICMIP; + imageParams.bits = IF_NOPICMIP | IF_RED; imageParams.filterType = filterType_t::FT_DEFAULT; imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; - tr.fogImage = R_CreateImage( "_fog", ( const byte ** ) &data, FOG_S, FOG_T, 1, imageParams ); + tr.fogImageNaive = R_CreateImage( "_fogNaive", ( const byte ** ) &data, FOG_S, FOG_T, 1, imageParams ); + + /* HACK: The previous fog image generator was calibrated for the + naive pipeline that was unaware of colorspaces. We need a new fog + image generator calibrated for the linear pipeline. It happens that + applying an sRGB-to-linear conversion of the alpha channel luckily + produces some good-enough results, and since we optimize the alpha + channel by storing it in a red-only image, this is very cheap to do. + A non-hacky implementation is welcome. */ + imageParams.bits = IF_NOPICMIP | IF_SRGB; + imageParams.bits |= glConfig2.textureSrgbR8Available ? IF_RED : 0; + + tr.fogImageLinear = R_CreateImage( "_fogLinear", ( const byte ** ) &data, FOG_S, FOG_T, 1, imageParams ); + ri.Hunk_FreeTempMemory( data ); - borderColor[ 0 ] = 1.0; - borderColor[ 1 ] = 1.0; - borderColor[ 2 ] = 1.0; - borderColor[ 3 ] = 1; + /* Just to be safe and not leave a null pointer in the wild. + This is modified when a map is loaded. */ + tr.fogImage = tr.fogImageNaive; + + vec4_t borderColor; + Vector4Set( borderColor, 1.0f, 1.0f, 1.0f, 1.0f ); glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor ); } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index c8d8759c90..c9ba9ebd1d 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -476,6 +476,7 @@ enum class ssaoMode { IF_DEPTH32 = BIT( 11 ), IF_PACKED_DEPTH24_STENCIL8 = BIT( 12 ), IF_LIGHTMAP = BIT( 13 ), + IF_RED = BIT( 14 ), IF_RGBE = BIT( 15 ), IF_ALPHATEST = BIT( 16 ), // FIXME: this is unused IF_ALPHA = BIT( 17 ), @@ -2449,7 +2450,9 @@ enum image_t *defaultImage; image_t *cinematicImage[ MAX_IN_GAME_VIDEOS ]; - image_t *fogImage; + image_t *fogImage; // Will be set to either fogImageNaive or fogImageSrgb. + image_t *fogImageNaive; + image_t *fogImageLinear; image_t *whiteImage; // full of 0xff image_t *blackImage; // full of 0x0 image_t *redImage; diff --git a/src/engine/renderer/tr_public.h b/src/engine/renderer/tr_public.h index b879c3fe7a..c9f9877c35 100644 --- a/src/engine/renderer/tr_public.h +++ b/src/engine/renderer/tr_public.h @@ -121,6 +121,7 @@ struct glconfig2_t bool gpuShader4Available; bool gpuShader5Available; bool textureGatherAvailable; + bool textureSrgbR8Available; int maxDrawBuffers; float maxTextureAnisotropy; diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index c59bb64b89..fa9bbcfc09 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4415,6 +4415,11 @@ static bool ParseShader( const char *_text ) return false; } + if ( tr.worldLinearizeTexture ) + { + convertFromSRGB( shader.fogParms.color ); + } + token = COM_ParseExt2( text, false ); if ( !token[ 0 ] ) diff --git a/src/engine/sys/sdl_glimp.cpp b/src/engine/sys/sdl_glimp.cpp index 501de885f1..ecc7f62fc3 100644 --- a/src/engine/sys/sdl_glimp.cpp +++ b/src/engine/sys/sdl_glimp.cpp @@ -123,6 +123,8 @@ static Cvar::Cvar r_ext_texture_integer( "r_ext_texture_integer", "Use GL_EXT_texture_integer if available", Cvar::NONE, true ); static Cvar::Cvar r_ext_texture_rg( "r_ext_texture_rg", "Use GL_EXT_texture_rg if available", Cvar::NONE, true ); +static Cvar::Cvar r_ext_texture_srgb_r8( "r_ext_texture_srgb_r8", + "Use GL_EXT_texture_sRGB_R8 if available", Cvar::NONE, true ); static Cvar::Cvar r_khr_debug( "r_khr_debug", "Use GL_KHR_debug if available", Cvar::NONE, true ); static Cvar::Cvar r_khr_shader_subgroup( "r_khr_shader_subgroup", @@ -2019,6 +2021,7 @@ static void GLimp_InitExtensions() Cvar::Latch( r_ext_texture_float ); Cvar::Latch( r_ext_texture_integer ); Cvar::Latch( r_ext_texture_rg ); + Cvar::Latch( r_ext_texture_srgb_r8 ); Cvar::Latch( r_khr_debug ); Cvar::Latch( r_khr_shader_subgroup ); @@ -2198,6 +2201,8 @@ static void GLimp_InitExtensions() // made required in OpenGL 3.0 glConfig2.textureCompressionRGTCAvailable = LOAD_EXTENSION( ExtFlag_CORE, ARB_texture_compression_rgtc ); + glConfig2.textureSrgbR8Available = LOAD_EXTENSION_WITH_TEST( ExtFlag_NONE, EXT_texture_sRGB_R8, r_ext_texture_srgb_r8.Get() ); + // Texture - others glConfig2.textureAnisotropyAvailable = false; glConfig2.textureAnisotropy = 0.0f;