Skip to content

Commit 3f48b8d

Browse files
committed
renderer: implement native sRGB support
1 parent 6ad9167 commit 3f48b8d

File tree

14 files changed

+511
-38
lines changed

14 files changed

+511
-38
lines changed

src/common/Color.h

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

41+
inline float convertFromSRGB( float f, bool accurate = true )
42+
{
43+
if ( accurate )
44+
{
45+
return f <= 0.04045f ? f * (1.0f / 12.92f) : pow((f + 0.055f) * (1.0f / 1.055f), 2.4f);
46+
}
47+
48+
return pow( f, 2.2f );
49+
}
50+
51+
inline void convertFromSRGB( float* v, bool accurate = true )
52+
{
53+
v[ 0 ] = convertFromSRGB( v[ 0 ], accurate );
54+
v[ 1 ] = convertFromSRGB( v[ 1 ], accurate );
55+
v[ 2 ] = convertFromSRGB( v[ 2 ], accurate );
56+
}
57+
58+
inline void convertFromSRGB( byte* bytes, bool accurate = true )
59+
{
60+
vec3_t v;
61+
VectorScale( bytes, 1.0f / 255.0f, v );
62+
convertFromSRGB( v, accurate );
63+
VectorScale( v, 255.0f, bytes );
64+
}
65+
4166
namespace Color {
4267

4368
/*
@@ -256,6 +281,22 @@ class BasicColor
256281
data_[ 3 ] = v;
257282
}
258283

284+
static component_type ConvertFromSRGB( component_type v, bool accurate = true ) NOEXCEPT
285+
{
286+
float f = float( v ) / float( component_max );
287+
f = convertFromSRGB( f, accurate );
288+
return component_type( f * float( component_max ) );
289+
}
290+
291+
BasicColor ConvertFromSRGB( bool accurate = true ) const NOEXCEPT
292+
{
293+
return BasicColor(
294+
ConvertFromSRGB( Red(), accurate ),
295+
ConvertFromSRGB( Green(), accurate ),
296+
ConvertFromSRGB( Blue(), accurate ),
297+
Alpha() );
298+
}
299+
259300
CONSTEXPR_FUNCTION_RELAXED BasicColor& operator*=( float factor ) NOEXCEPT
260301
{
261302
*this = *this * factor;

src/engine/renderer/gl_shader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,11 @@ static std::string GenEngineConstants() {
759759
AddConst( str, "r_RimExponent", r_rimExponent->value );
760760
}
761761

762+
if ( r_accurateSRGB.Get() )
763+
{
764+
AddDefine( str, "r_accurateSRGB", 1 );
765+
}
766+
762767
if ( r_showLightTiles->integer )
763768
{
764769
AddDefine( str, "r_showLightTiles", 1 );
@@ -2801,6 +2806,7 @@ GLShader_cameraEffects::GLShader_cameraEffects() :
28012806
u_CurrentMap( this ),
28022807
u_GlobalLightFactor( this ),
28032808
u_ColorModulate( this ),
2809+
u_SRGB( this ),
28042810
u_Tonemap( this ),
28052811
u_TonemapParms( this ),
28062812
u_TonemapExposure( this ),

src/engine/renderer/gl_shader.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3073,6 +3073,18 @@ class u_InverseGamma :
30733073
}
30743074
};
30753075

3076+
class u_SRGB :
3077+
GLUniform1Bool {
3078+
public:
3079+
u_SRGB( GLShader* shader ) :
3080+
GLUniform1Bool( shader, "u_SRGB", true ) {
3081+
}
3082+
3083+
void SetUniform_SRGB( bool tonemap ) {
3084+
this->SetValue( tonemap );
3085+
}
3086+
};
3087+
30763088
class u_Tonemap :
30773089
GLUniform1Bool {
30783090
public:
@@ -3579,6 +3591,7 @@ class GLShader_cameraEffects :
35793591
public u_CurrentMap,
35803592
public u_GlobalLightFactor,
35813593
public u_ColorModulate,
3594+
public u_SRGB,
35823595
public u_Tonemap,
35833596
public u_TonemapParms,
35843597
public u_TonemapExposure,

src/engine/renderer/glsl_source/cameraEffects_fp.glsl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ uniform sampler3D u_ColorMap3D;
3131
uniform vec4 u_ColorModulate;
3232
uniform float u_GlobalLightFactor; // 1 / tr.identityLight
3333
uniform float u_InverseGamma;
34+
uniform bool u_SRGB;
35+
36+
void convertToSRGB(inout vec3 color) {
37+
#if defined(r_accurateSRGB)
38+
float threshold = 0.0031308f;
39+
40+
bvec3 cutoff = lessThan(color, vec3(threshold));
41+
vec3 low = vec3(12.92f) * color;
42+
vec3 high = vec3(1.055f) * pow(color, vec3(1.0f / 2.4f)) - vec3(0.055f);
43+
44+
color = mix(high, low, cutoff);
45+
#else
46+
float inverse = 0.4545454f; // 1 / 2.2
47+
color = pow(color, vec3(inverse));
48+
#endif
49+
}
3450

3551
// Tone mapping is not available when high-precision float framebuffer isn't enabled or supported.
3652
#if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float)
@@ -59,6 +75,11 @@ void main()
5975
vec4 color = texture2D(u_CurrentMap, st);
6076
color *= u_GlobalLightFactor;
6177

78+
if ( u_SRGB )
79+
{
80+
convertToSRGB( color.rgb );
81+
}
82+
6283
#if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float)
6384
if( u_Tonemap ) {
6485
color.rgb = TonemapLottes( color.rgb * u_TonemapExposure );

src/engine/renderer/tr_backend.cpp

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,99 @@ void GL_VertexAttribPointers( uint32_t attribBits )
707707
}
708708
}
709709

710+
static GLint GL_ToSRGB( GLint internalFormat, bool isSRGB )
711+
{
712+
if ( !isSRGB )
713+
{
714+
return internalFormat;
715+
}
716+
717+
auto convert = []( GLint format )
718+
{
719+
switch ( format )
720+
{
721+
#if 0 // Not used in the code base.
722+
/* EXT_texture_sRGB_R8 extension.
723+
See: https://github.com/KhronosGroup/OpenGL-Registry/blob/main/extensions/EXT/EXT_texture_sRGB_R8.txt */
724+
case GL_RED:
725+
return GL_SR8_EXT;
726+
#endif
727+
case GL_RGB:
728+
return GL_SRGB;
729+
case GL_RGBA:
730+
return GL_SRGB_ALPHA;
731+
case GL_RGB8:
732+
return GL_SRGB8;
733+
case GL_RGBA8:
734+
return GL_SRGB8_ALPHA8;
735+
#if 0 // Internal formats, should not be used directly.
736+
case GL_COMPRESSED_RGB:
737+
return GL_COMPRESSED_SRGB;
738+
case GL_COMPRESSED_RGBA:
739+
return GL_COMPRESSED_SRGB_ALPHA;
740+
#endif
741+
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
742+
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
743+
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
744+
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
745+
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
746+
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
747+
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
748+
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
749+
#if 0 // Not used in the codebase,
750+
/* Core 4.2, ARB_texture_compression_bptc extension.
751+
See: https://github.com/KhronosGroup/OpenGL-Registry/blob/main/extensions/ARB/ARB_texture_compression_bptc.txt */
752+
case GL_COMPRESSED_RGBA_BPTC_UNORM:
753+
return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
754+
#endif
755+
default:
756+
return format;
757+
}
758+
};
759+
760+
GLint finalFormat = convert( internalFormat );
761+
762+
if ( finalFormat == internalFormat )
763+
{
764+
Log::Warn( "Missing sRGB conversion for GL format: %0#x", internalFormat );
765+
}
766+
else
767+
{
768+
Log::Debug( "Using sRGB GL format: %0#x", finalFormat );
769+
}
770+
771+
return finalFormat;
772+
}
773+
774+
void GL_TexImage2D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *data, bool isSRGB )
775+
{
776+
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );
777+
778+
glTexImage2D( target, level, finalFormat, width, height, border, format, type, data );
779+
780+
}
781+
782+
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 )
783+
{
784+
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );
785+
786+
glTexImage3D( target, level, finalFormat, width, height, depth, border, format, type, data );
787+
}
788+
789+
void GL_CompressedTexImage2D( GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data, bool isSRGB )
790+
{
791+
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );
792+
793+
glCompressedTexImage2D( target, level, finalFormat, width, height, border, imageSize, data );
794+
}
795+
796+
void GL_CompressedTexSubImage3D( GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLsizei size, const void *data, bool isSRGB )
797+
{
798+
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );
799+
800+
glCompressedTexSubImage3D( target, level, xOffset, yOffset, zOffset, width, height, depth, finalFormat, size, data );
801+
}
802+
710803
/*
711804
================
712805
RB_Hyperspace
@@ -1535,6 +1628,8 @@ void RB_CameraPostFX() {
15351628

15361629
gl_cameraEffectsShader->SetUniform_InverseGamma( 1.0 / r_gamma->value );
15371630

1631+
gl_cameraEffectsShader->SetUniform_SRGB( tr.worldLinearizeTexture );
1632+
15381633
const bool tonemap = r_toneMapping.Get() && r_highPrecisionRendering.Get() && glConfig2.textureFloatAvailable;
15391634
if ( tonemap ) {
15401635
vec4_t tonemapParms { r_toneMappingContrast.Get(), r_toneMappingHighlightsCompressionSpeed.Get() };
@@ -2668,12 +2763,24 @@ void RE_UploadCinematic( int cols, int rows, const byte *data, int client, bool
26682763
GL_Bind( tr.cinematicImage[ client ] );
26692764

26702765
// if the scratchImage isn't in the format we want, specify it as a new texture
2766+
/* HACK: This also detects we start playing a video to set the appropriate colorspace
2767+
because this assumes a video cannot have a 1×1 size (the RoQ format expects the size
2768+
to be a multiples of 4). */
26712769
if ( cols != tr.cinematicImage[ client ]->width || rows != tr.cinematicImage[ client ]->height )
26722770
{
26732771
tr.cinematicImage[ client ]->width = tr.cinematicImage[ client ]->uploadWidth = cols;
26742772
tr.cinematicImage[ client ]->height = tr.cinematicImage[ client ]->uploadHeight = rows;
26752773

2676-
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
2774+
bool isSRGB = tr.worldLinearizeTexture;
2775+
2776+
// Makes sure listImages lists the colorspace properly.
2777+
if ( isSRGB )
2778+
{
2779+
tr.cinematicImage[ client ]->bits |= IF_SRGB;
2780+
}
2781+
// No need to delete the bit otherwise because R_InitImages() is called at every map load.
2782+
2783+
GL_TexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data, isSRGB );
26772784

26782785
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
26792786
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
@@ -2903,7 +3010,10 @@ const RenderCommand *Poly2dCommand::ExecuteSelf( ) const
29033010
tess.verts[ tess.numVertexes ].texCoords[ 0 ] = verts[ i ].st[ 0 ];
29043011
tess.verts[ tess.numVertexes ].texCoords[ 1 ] = verts[ i ].st[ 1 ];
29053012

2906-
tess.verts[ tess.numVertexes ].color = Color::Adapt( verts[ i ].modulate );
3013+
Color::Color32Bit color = Color::Adapt( verts[ i ].modulate );
3014+
color = tr.convertColorFromSRGB( color );
3015+
tess.verts[ tess.numVertexes ].color = color;
3016+
29073017
tess.numVertexes++;
29083018
}
29093019

@@ -2960,7 +3070,10 @@ const RenderCommand *Poly2dIndexedCommand::ExecuteSelf( ) const
29603070
tess.verts[ tess.numVertexes ].texCoords[ 0 ] = verts[ i ].st[ 0 ];
29613071
tess.verts[ tess.numVertexes ].texCoords[ 1 ] = verts[ i ].st[ 1 ];
29623072

2963-
tess.verts[ tess.numVertexes ].color = Color::Adapt( verts[ i ].modulate );
3073+
Color::Color32Bit color = Color::Adapt( verts[ i ].modulate );
3074+
color = tr.convertColorFromSRGB( color );
3075+
tess.verts[ tess.numVertexes ].color = color;
3076+
29643077
tess.numVertexes++;
29653078
}
29663079

0 commit comments

Comments
 (0)