Skip to content

renderer: implement native sRGB support #1687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/common/Color.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Compiler.h"
#include "Math.h"

#define convertFromSRGB( v ) (v <= 0.04045f ? v * (1.0f / 12.92f) : pow((v + 0.055f) * (1.0f / 1.055f), 2.4f))

namespace Color {

/*
Expand Down Expand Up @@ -256,6 +258,20 @@ class BasicColor
data_[ 3 ] = v;
}

CONSTEXPR_FUNCTION_RELAXED component_type ConvertFromSRGB( component_type v ) NOEXCEPT
{
float f = float( v ) / 255.0f;
f = convertFromSRGB( f );
return component_type( f * 255 );
}

CONSTEXPR_FUNCTION_RELAXED void ConvertFromSRGB() NOEXCEPT
{
SetRed( ConvertFromSRGB( Red() ) );
SetGreen( ConvertFromSRGB( Green() ) );
SetBlue( ConvertFromSRGB( Blue() ) );
}

CONSTEXPR_FUNCTION_RELAXED BasicColor& operator*=( float factor ) NOEXCEPT
{
*this = *this * factor;
Expand Down
76 changes: 75 additions & 1 deletion src/engine/renderer/tr_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,78 @@ void GL_VertexAttribPointers( uint32_t attribBits )
}
}

GLint GL_ToSRGB_( GLint internalFormat, bool isSRGB )
{
if ( !isSRGB )
{
return internalFormat;
}

switch ( internalFormat )
{
case GL_RGB:
return GL_SRGB;
case GL_RGBA:
return GL_SRGB_ALPHA;
case GL_RGB8:
return GL_SRGB8;
case GL_RGBA8:
return GL_SRGB8_ALPHA8;
// not used
// case GL_COMPRESSED_RGB:
// return GL_COMPRESSED_SRGB;
case GL_COMPRESSED_RGBA:
return GL_COMPRESSED_SRGB_ALPHA;
// not used, core 4.2, ARB_texture_compression_bptc
// case GL_COMPRESSED_RGBA_BPTC_UNORM:
// return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
default:
return internalFormat;
}
}

GLint GL_ToSRGB( GLint internalFormat, bool isSRGB )
{
GLint finalFormat = GL_ToSRGB_( internalFormat, isSRGB );

if ( isSRGB )
{
if ( finalFormat == internalFormat )
{
Log::Warn( "Missing sRGB conversion for GL format: %0#x", internalFormat );
}
else
{
Log::Debug( "Using sRGB GL format: %0#x", finalFormat );
}
}

return finalFormat;
}

void GL_TexImage2D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data, bool isSRGB )
{
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );

glTexImage2D( target, level, finalFormat, width, height, border, format, type, data );

}

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 )
{
GLint finalFormat = GL_ToSRGB( internalFormat, isSRGB );

glTexImage3D( target, level, finalFormat, width, height, depth, border, format, type, data );
}

/*
================
RB_Hyperspace
Expand Down Expand Up @@ -1289,7 +1361,9 @@ void RB_RenderGlobalFog()
void RB_RenderBloom()
{
if ( ( backEnd.refdef.rdflags & ( RDF_NOWORLDMODEL | RDF_NOBLOOM ) )
|| !glConfig2.bloom || backEnd.viewParms.portalLevel > 0 ) {
|| !glConfig2.bloom || backEnd.viewParms.portalLevel > 0
|| !tr.worldLinearizeTexture )
{
return;
}

Expand Down
96 changes: 91 additions & 5 deletions src/engine/renderer/tr_bsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,15 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
return;
}

int lightmapBits = IF_LIGHTMAP | IF_NOPICMIP;
int deluxemapBits = IF_NORMALMAP | IF_NOPICMIP;

if ( tr.worldLinearizeLightMap )
{
lightmapBits |= IF_SRGB;
deluxemapBits |= IF_SRGB;
}

int len = l->filelen;
if ( !len )
{
Expand Down Expand Up @@ -493,7 +502,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
LoadRGBEToBytes( va( "%s/%s", mapName, filename.c_str() ), &ldrImage, &width, &height );

imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP | IF_LIGHTMAP;
imageParams.bits = lightmapBits;
imageParams.filterType = filterType_t::FT_DEFAULT;
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;

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

imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP | IF_NORMALMAP;
imageParams.bits = deluxemapBits;
imageParams.filterType = filterType_t::FT_DEFAULT;
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;

Expand Down Expand Up @@ -549,7 +558,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )

if (!tr.worldDeluxeMapping || i % 2 == 0) {
imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP | IF_LIGHTMAP;
imageParams.bits = lightmapBits;
imageParams.filterType = filterType_t::FT_LINEAR;
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;

Expand All @@ -559,7 +568,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
else if (tr.worldDeluxeMapping)
{
imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP | IF_NORMALMAP;
imageParams.bits = deluxemapBits;
imageParams.filterType = filterType_t::FT_LINEAR;
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;

Expand Down Expand Up @@ -629,7 +638,7 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName )
}

imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP | IF_LIGHTMAP;
imageParams.bits = lightmapBits;
imageParams.filterType = filterType_t::FT_DEFAULT;
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;

Expand Down Expand Up @@ -972,6 +981,11 @@ static void ParseTriangleSurface( dsurface_t* ds, drawVert_t* verts, bspSurface_

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

if ( tr.worldLinearizeLightMap )
{
cv->verts[ i ].lightColor.ConvertFromSRGB();
}

if ( tr.overbrightBits < tr.mapOverBrightBits ) {
R_ColorShiftLightingBytes( cv->verts[ i ].lightColor.ToArray() );
}
Expand Down Expand Up @@ -3502,6 +3516,12 @@ void R_LoadLightGrid( lump_t *l )
{
ambientColor[ j ] = tmpAmbient[ j ] * ( 1.0f / 255.0f );
directedColor[ j ] = tmpDirected[ j ] * ( 1.0f / 255.0f );

if ( tr.worldLinearizeLightMap )
{
ambientColor[ j ] = convertFromSRGB( ambientColor[ j ] );
directedColor[ j ] = convertFromSRGB( directedColor[ j ] );
}
}

const float forceAmbient = r_forceAmbient.Get();
Expand Down Expand Up @@ -3766,6 +3786,70 @@ void R_LoadEntities( lump_t *l, std::string &externalEntities )
tr.worldDeluxeMapping = glConfig2.deluxeMapping;
}

bool sRGBtex = false;
bool sRGBcolor = false;
bool sRGBlight = false;

s = strstr( value, "-sRGB" );

if ( s && ( s[5] == ' ' || s[5] == '\0' ) )
{
sRGBtex = true;
sRGBcolor = true;
sRGBlight = true;
}

s = strstr( value, "-nosRGB" );

if ( s && ( s[5] == ' ' || s[5] == '\0' ) )
{
sRGBtex = false;
sRGBcolor = false;
sRGBlight = true;
}

if ( strstr( value, "-sRGBlight" ) )
{
sRGBlight = true;
}

if ( strstr( value, "-nosRGBlight" ) )
{
sRGBlight = false;
}

if ( strstr( value, "-sRGBcolor" ) )
{
sRGBcolor = true;
}

if ( strstr( value, "-nosRGBcolor" ) )
{
sRGBcolor = false;
}

if ( strstr( value, "-sRGBtex" ) )
{
sRGBtex = true;
}

if ( strstr( value, "-nosRGBtex" ) )
{
sRGBtex = false;
}

if ( sRGBlight )
{
Log::Debug("map features lights in sRGB colorspace" );
tr.worldLinearizeLightMap = true;
}

if ( sRGBcolor && sRGBtex )
{
Log::Debug("map features lights computed with linear colors and textures" );
tr.worldLinearizeTexture = true;
}

continue;
}

Expand Down Expand Up @@ -4517,6 +4601,8 @@ void RE_LoadWorldMap( const char *name )
tr.overbrightBits = std::min( tr.mapOverBrightBits, r_overbrightBits.Get() ); // set by RE_LoadWorldMap
tr.mapLightFactor = 1.0f; // set by RE_LoadWorldMap
tr.identityLight = 1.0f; // set by RE_LoadWorldMap
tr.worldLinearizeTexture = false;
tr.worldLinearizeLightMap = false;

s_worldData = {};
Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) );
Expand Down
20 changes: 10 additions & 10 deletions src/engine/renderer/tr_image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,7 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int
GLenum target;
GLenum format = GL_RGBA;
GLenum internalFormat = GL_RGB;
bool isSRGB = image->bits & IF_SRGB;

static const vec4_t oneClampBorder = { 1, 1, 1, 1 };
static const vec4_t zeroClampBorder = { 0, 0, 0, 1 };
Expand Down Expand Up @@ -1076,9 +1077,7 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int
mipLayers = numLayers;

for( i = 0; i < numMips; i++ ) {
glTexImage3D( GL_TEXTURE_3D, i, internalFormat,
scaledWidth, scaledHeight, mipLayers,
0, format, GL_UNSIGNED_BYTE, nullptr );
GL_TexImage3D( GL_TEXTURE_3D, i, internalFormat, scaledWidth, scaledHeight, mipLayers, 0, format, GL_UNSIGNED_BYTE, nullptr, isSRGB );

if( mipWidth > 1 ) mipWidth >>= 1;
if( mipHeight > 1 ) mipHeight >>= 1;
Expand Down Expand Up @@ -1144,18 +1143,17 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int
}
break;
case GL_TEXTURE_CUBE_MAP:
glTexImage2D( target + i, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE,
scaledBuffer );
GL_TexImage2D( target + i, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, scaledBuffer, isSRGB );
break;

default:
if ( image->bits & IF_PACKED_DEPTH24_STENCIL8 )
{
glTexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_INT_24_8, nullptr );
GL_TexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_INT_24_8, nullptr, isSRGB );
}
else
{
glTexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, scaledBuffer );
GL_TexImage2D( target, 0, internalFormat, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, scaledBuffer, isSRGB );
}

break;
Expand Down Expand Up @@ -1511,7 +1509,9 @@ image_t *R_CreateGlyph( const char *name, const byte *pic, int width, int height
image->uploadHeight = height;
image->internalFormat = GL_RGBA;

glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pic );
bool isSRGB = true;

GL_TexImage2D( GL_TEXTURE_2D, 0, image->internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pic, isSRGB );

GL_CheckErrors();

Expand Down Expand Up @@ -2734,7 +2734,7 @@ static void R_CreateColorGradeImage()
}

imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP;
imageParams.bits = IF_NOPICMIP | IF_SRGB;
imageParams.filterType = filterType_t::FT_LINEAR;
imageParams.wrapType = wrapTypeEnum_t::WT_EDGE_CLAMP;

Expand Down Expand Up @@ -3007,7 +3007,7 @@ qhandle_t RE_GenerateTexture( const byte *pic, int width, int height )
std::string name = Str::Format( "$generatedTexture%d", tr.numGeneratedTextures++ );

imageParams_t imageParams = {};
imageParams.bits = IF_NOPICMIP;
imageParams.bits = IF_NOPICMIP | IF_SRGB;
imageParams.filterType = filterType_t::FT_LINEAR;
imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP;

Expand Down
2 changes: 2 additions & 0 deletions src/engine/renderer/tr_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,8 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p
GL_fboShim.glBindRenderbuffer( GL_RENDERBUFFER, 0 );
glState.currentFBO = nullptr;

glEnable( GL_FRAMEBUFFER_SRGB );

GL_PolygonMode( GL_FRONT_AND_BACK, GL_FILL );
GL_DepthMask( GL_TRUE );
glDisable( GL_DEPTH_TEST );
Expand Down
7 changes: 7 additions & 0 deletions src/engine/renderer/tr_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ enum class ssaoMode {
IF_RGBE = BIT( 15 ),
IF_ALPHATEST = BIT( 16 ), // FIXME: this is unused
IF_ALPHA = BIT( 17 ),
IF_SRGB = BIT( 18 ),
IF_BC1 = BIT( 19 ),
IF_BC2 = BIT( 20 ),
IF_BC3 = BIT( 21 ),
Expand Down Expand Up @@ -1146,6 +1147,7 @@ enum class ssaoMode {

expression_t deformMagnitudeExp;

bool specularSRGB;
bool noFog; // used only for shaders that have fog disabled, so we can enable it for individual stages

bool useMaterialSystem = false;
Expand Down Expand Up @@ -2513,6 +2515,8 @@ enum class ssaoMode {
bool worldLightMapping;
bool worldDeluxeMapping;
bool worldHDR_RGBE;
bool worldLinearizeTexture;
bool worldLinearizeLightMap;

lightMode_t lightMode;
lightMode_t worldLight;
Expand Down Expand Up @@ -3020,6 +3024,9 @@ inline bool checkGLErrors()
void GL_VertexAttribsState( uint32_t stateBits );
void GL_VertexAttribPointers( uint32_t attribBits );
void GL_Cull( cullType_t cullType );
GLint GL_ToSRGB( GLint internalFormat, bool isSRGB );
void GL_TexImage2D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data, bool isSRGB );
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 );
void R_ShutdownBackend();

/*
Expand Down
Loading