Skip to content

Commit acceb04

Browse files
committed
renderer: implement native sRGB support
1 parent 6353c46 commit acceb04

File tree

8 files changed

+268
-14
lines changed

8 files changed

+268
-14
lines changed

src/common/Color.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3838
#include "Compiler.h"
3939
#include "Math.h"
4040

41+
#define convertFromSRGB( v ) (v <= 0.04045f ? v * (1.0f / 12.92f) : pow((v + 0.055f) * (1.0f / 1.055f), 2.4f))
42+
4143
namespace Color {
4244

4345
/*
@@ -256,6 +258,20 @@ class BasicColor
256258
data_[ 3 ] = v;
257259
}
258260

261+
CONSTEXPR_FUNCTION_RELAXED component_type ConvertFromSRGB( component_type v ) NOEXCEPT
262+
{
263+
float f = float( v ) / 255.0f;
264+
f = convertFromSRGB( f );
265+
return component_type( f * 255 );
266+
}
267+
268+
CONSTEXPR_FUNCTION_RELAXED void ConvertFromSRGB() NOEXCEPT
269+
{
270+
SetRed( ConvertFromSRGB( Red() ) );
271+
SetGreen( ConvertFromSRGB( Green() ) );
272+
SetBlue( ConvertFromSRGB( Blue() ) );
273+
}
274+
259275
CONSTEXPR_FUNCTION_RELAXED BasicColor& operator*=( float factor ) NOEXCEPT
260276
{
261277
*this = *this * factor;

src/engine/renderer/tr_backend.cpp

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,78 @@ void GL_VertexAttribPointers( uint32_t attribBits )
707707
}
708708
}
709709

710+
static GLint GL_ToSRGB( GLint internalFormat, bool isSRGB )
711+
{
712+
auto convert = []( GLint format )
713+
{
714+
switch ( format )
715+
{
716+
case GL_RGB:
717+
return GL_SRGB;
718+
case GL_RGBA:
719+
return GL_SRGB_ALPHA;
720+
case GL_RGB8:
721+
return GL_SRGB8;
722+
case GL_RGBA8:
723+
return GL_SRGB8_ALPHA8;
724+
case GL_COMPRESSED_RGB:
725+
return GL_COMPRESSED_SRGB;
726+
case GL_COMPRESSED_RGBA:
727+
return GL_COMPRESSED_SRGB_ALPHA;
728+
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
729+
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
730+
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
731+
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
732+
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
733+
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
734+
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
735+
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
736+
/* Not used in codebase, Core 4.2, ARB_texture_compression_bptc extension.
737+
case GL_COMPRESSED_RGBA_BPTC_UNORM:
738+
return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
739+
*/
740+
default:
741+
return format;
742+
}
743+
};
744+
745+
if ( !isSRGB )
746+
{
747+
return internalFormat;
748+
}
749+
750+
GLint finalFormat = convert( internalFormat );
751+
752+
if ( isSRGB )
753+
{
754+
if ( finalFormat == internalFormat )
755+
{
756+
Log::Warn( "Missing sRGB conversion for GL format: %0#x", internalFormat );
757+
}
758+
else
759+
{
760+
Log::Debug( "Using sRGB GL format: %0#x", finalFormat );
761+
}
762+
}
763+
764+
return finalFormat;
765+
}
766+
767+
void GL_TexImage2D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data, bool isSRGB )
768+
{
769+
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );
770+
771+
glTexImage2D( target, level, finalFormat, width, height, border, format, type, data );
772+
773+
}
774+
775+
void GL_TexImage3D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * data, bool isSRGB )
776+
{
777+
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );
778+
779+
glTexImage3D( target, level, finalFormat, width, height, depth, border, format, type, data );
780+
}
781+
710782
/*
711783
================
712784
RB_Hyperspace
@@ -781,6 +853,9 @@ static void RB_SetGL2D()
781853

782854
backEnd.projection2D = true;
783855

856+
// Always render and blend the UI in non-linear space like HTML does.
857+
glDisable( GL_FRAMEBUFFER_SRGB );
858+
784859
// set 2D virtual screen size
785860
GL_Viewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight );
786861
GL_Scissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight );
@@ -1289,7 +1364,9 @@ void RB_RenderGlobalFog()
12891364
void RB_RenderBloom()
12901365
{
12911366
if ( ( backEnd.refdef.rdflags & ( RDF_NOWORLDMODEL | RDF_NOBLOOM ) )
1292-
|| !glConfig2.bloom || backEnd.viewParms.portalLevel > 0 ) {
1367+
|| !glConfig2.bloom || backEnd.viewParms.portalLevel > 0
1368+
|| !tr.worldLinearizeTexture )
1369+
{
12931370
return;
12941371
}
12951372

@@ -1524,6 +1601,14 @@ void RB_CameraPostFX() {
15241601

15251602
GLIMP_LOGCOMMENT( "--- RB_CameraPostFX ---" );
15261603

1604+
/* HACK: Do not compute the colorgrade in linear space,
1605+
it doesn't work even when we set IF_SRGB image bit
1606+
in R_CreateColorGradeImage() in tr_image.cpp. */
1607+
if ( tr.worldLinearizeTexture )
1608+
{
1609+
glDisable( GL_FRAMEBUFFER_SRGB );
1610+
}
1611+
15271612
GL_State( GLS_DEPTHTEST_DISABLE );
15281613
GL_Cull( cullType_t::CT_TWO_SIDED );
15291614

@@ -1558,6 +1643,12 @@ void RB_CameraPostFX() {
15581643

15591644
Tess_InstantScreenSpaceQuad();
15601645

1646+
// HACK: Revert the hack (see above).
1647+
if ( tr.worldLinearizeTexture )
1648+
{
1649+
glEnable( GL_FRAMEBUFFER_SRGB );
1650+
}
1651+
15611652
GL_CheckErrors();
15621653
}
15631654

@@ -3578,6 +3669,15 @@ const RenderCommand *SwapBuffersCommand::ExecuteSelf( ) const
35783669

35793670
backEnd.projection2D = false;
35803671

3672+
if ( tr.worldLinearizeTexture )
3673+
{
3674+
/* We always render the UI in non-linear space like HTML does
3675+
so we have to re-enable the linear space after the UI being
3676+
rendered when the map requires the world to be rendered in
3677+
linear space. */
3678+
glEnable( GL_FRAMEBUFFER_SRGB );
3679+
}
3680+
35813681
return this + 1;
35823682
}
35833683

src/engine/renderer/tr_bsp.cpp

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,14 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
451451
return;
452452
}
453453

454+
int lightmapBits = IF_LIGHTMAP | IF_NOPICMIP;
455+
int deluxemapBits = IF_NORMALMAP | IF_NOPICMIP;
456+
457+
if ( tr.worldLinearizeLightMap )
458+
{
459+
lightmapBits |= IF_SRGB;
460+
}
461+
454462
int len = l->filelen;
455463
if ( !len )
456464
{
@@ -493,7 +501,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
493501
LoadRGBEToBytes( va( "%s/%s", mapName, filename.c_str() ), &ldrImage, &width, &height );
494502

495503
imageParams_t imageParams = {};
496-
imageParams.bits = IF_NOPICMIP | IF_LIGHTMAP;
504+
imageParams.bits = lightmapBits;
497505
imageParams.filterType = filterType_t::FT_DEFAULT;
498506
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;
499507

@@ -520,7 +528,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
520528
Log::Debug("...loading external lightmap '%s/%s'", mapName, filename);
521529

522530
imageParams_t imageParams = {};
523-
imageParams.bits = IF_NOPICMIP | IF_NORMALMAP;
531+
imageParams.bits = deluxemapBits;
524532
imageParams.filterType = filterType_t::FT_DEFAULT;
525533
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;
526534

@@ -549,7 +557,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
549557

550558
if (!tr.worldDeluxeMapping || i % 2 == 0) {
551559
imageParams_t imageParams = {};
552-
imageParams.bits = IF_NOPICMIP | IF_LIGHTMAP;
560+
imageParams.bits = lightmapBits;
553561
imageParams.filterType = filterType_t::FT_LINEAR;
554562
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;
555563

@@ -559,7 +567,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
559567
else if (tr.worldDeluxeMapping)
560568
{
561569
imageParams_t imageParams = {};
562-
imageParams.bits = IF_NOPICMIP | IF_NORMALMAP;
570+
imageParams.bits = deluxemapBits;
563571
imageParams.filterType = filterType_t::FT_LINEAR;
564572
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;
565573

@@ -629,7 +637,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
629637
}
630638

631639
imageParams_t imageParams = {};
632-
imageParams.bits = IF_NOPICMIP | IF_LIGHTMAP;
640+
imageParams.bits = lightmapBits;
633641
imageParams.filterType = filterType_t::FT_DEFAULT;
634642
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;
635643

@@ -972,6 +980,11 @@ static void ParseTriangleSurface( dsurface_t* ds, drawVert_t* verts, bspSurface_
972980

973981
cv->verts[ i ].lightColor = Color::Adapt( verts[ i ].color );
974982

983+
if ( tr.worldLinearizeLightMap )
984+
{
985+
cv->verts[ i ].lightColor.ConvertFromSRGB();
986+
}
987+
975988
if ( tr.overbrightBits < tr.mapOverBrightBits ) {
976989
R_ColorShiftLightingBytes( cv->verts[ i ].lightColor.ToArray() );
977990
}
@@ -1206,6 +1219,11 @@ static void ParseMesh( dsurface_t *ds, drawVert_t *verts, bspSurface_t *surf )
12061219

12071220
points[ i ].lightColor = Color::Adapt( verts[ i ].color );
12081221

1222+
if ( tr.worldLinearizeLightMap )
1223+
{
1224+
points[ i ].lightColor.ConvertFromSRGB();
1225+
}
1226+
12091227
if ( tr.overbrightBits < tr.mapOverBrightBits )
12101228
{
12111229
R_ColorShiftLightingBytes( points[ i ].lightColor.ToArray() );
@@ -3502,6 +3520,12 @@ void R_LoadLightGrid( lump_t *l )
35023520
{
35033521
ambientColor[ j ] = tmpAmbient[ j ] * ( 1.0f / 255.0f );
35043522
directedColor[ j ] = tmpDirected[ j ] * ( 1.0f / 255.0f );
3523+
3524+
if ( tr.worldLinearizeLightMap )
3525+
{
3526+
ambientColor[ j ] = convertFromSRGB( ambientColor[ j ] );
3527+
directedColor[ j ] = convertFromSRGB( directedColor[ j ] );
3528+
}
35053529
}
35063530

35073531
const float forceAmbient = r_forceAmbient.Get();
@@ -3766,6 +3790,75 @@ void R_LoadEntities( lump_t *l, std::string &externalEntities )
37663790
tr.worldDeluxeMapping = glConfig2.deluxeMapping;
37673791
}
37683792

3793+
bool sRGBtex = false;
3794+
bool sRGBcolor = false;
3795+
bool sRGBlight = false;
3796+
3797+
s = strstr( value, "-sRGB" );
3798+
3799+
if ( s && ( s[5] == ' ' || s[5] == '\0' ) )
3800+
{
3801+
sRGBtex = true;
3802+
sRGBcolor = true;
3803+
sRGBlight = true;
3804+
}
3805+
3806+
s = strstr( value, "-nosRGB" );
3807+
3808+
if ( s && ( s[5] == ' ' || s[5] == '\0' ) )
3809+
{
3810+
sRGBtex = false;
3811+
sRGBcolor = false;
3812+
sRGBlight = false;
3813+
}
3814+
3815+
if ( strstr( value, "-sRGBlight" ) )
3816+
{
3817+
sRGBlight = true;
3818+
}
3819+
3820+
if ( strstr( value, "-nosRGBlight" ) )
3821+
{
3822+
sRGBlight = false;
3823+
}
3824+
3825+
if ( strstr( value, "-sRGBcolor" ) )
3826+
{
3827+
sRGBcolor = true;
3828+
}
3829+
3830+
if ( strstr( value, "-nosRGBcolor" ) )
3831+
{
3832+
sRGBcolor = false;
3833+
}
3834+
3835+
if ( strstr( value, "-sRGBtex" ) )
3836+
{
3837+
sRGBtex = true;
3838+
}
3839+
3840+
if ( strstr( value, "-nosRGBtex" ) )
3841+
{
3842+
sRGBtex = false;
3843+
}
3844+
3845+
if ( sRGBlight )
3846+
{
3847+
Log::Debug( "Map features lights in sRGB colorspace." );
3848+
tr.worldLinearizeLightMap = true;
3849+
}
3850+
3851+
if ( sRGBcolor && sRGBtex )
3852+
{
3853+
Log::Debug( "Map features lights computed with linear colors and textures." );
3854+
tr.worldLinearizeTexture = true;
3855+
}
3856+
else if ( sRGBcolor != sRGBtex )
3857+
{
3858+
Log::Warn( "Map features lights computed with a mix of linear and non-linear colors or textures, acting like both colors and textures were linear when lights were computed." );
3859+
tr.worldLinearizeTexture = true;
3860+
}
3861+
37693862
continue;
37703863
}
37713864

@@ -4517,6 +4610,8 @@ void RE_LoadWorldMap( const char *name )
45174610
tr.overbrightBits = std::min( tr.mapOverBrightBits, r_overbrightBits.Get() ); // set by RE_LoadWorldMap
45184611
tr.mapLightFactor = 1.0f; // set by RE_LoadWorldMap
45194612
tr.identityLight = 1.0f; // set by RE_LoadWorldMap
4613+
tr.worldLinearizeTexture = false;
4614+
tr.worldLinearizeLightMap = false;
45204615

45214616
s_worldData = {};
45224617
Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) );

0 commit comments

Comments
 (0)