Skip to content

Commit

Permalink
PBR renderer: improved handling of sRGB textures
Browse files Browse the repository at this point in the history
Also added option to convert shader output to sRGB color space
  • Loading branch information
TheMostDiligent committed Sep 16, 2023
1 parent caaad88 commit 8e70179
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 96 deletions.
25 changes: 25 additions & 0 deletions PBR/interface/PBR_Renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class PBR_Renderer
/// instead of a combined physical description texture.
bool UseSeparateMetallicRoughnessTextures = false;

/// Manually convert shader output to sRGB color space.
bool ConvertOutputToSRGB = false;

static const SamplerDesc DefaultSampler;

/// Immutable sampler for color map texture.
Expand Down Expand Up @@ -108,6 +111,28 @@ class PBR_Renderer

/// Optional input layout description.
InputLayoutDesc InputLayout;

/// Conversion mode applied to diffuse, specular and emissive textures.
///
/// \note Normal map, ambient occlusion and physical description textures are
/// always assumed to be in linear color space.
enum TEX_COLOR_CONVERSION_MODE
{
/// Sampled texture colors are used as is.
///
/// \remarks This mode should be used if the textures are in linear color space,
/// or if the texture is in sRGB color space and the texture view is
/// also in sRGB color space (which ensures that sRGB->linear conversion
/// is performed by the GPU).
TEX_COLOR_CONVERSION_MODE_NONE = 0,

/// Manually convert texture colors from sRGB to linear color space.
///
/// \remarks This mode should be used if the textures are in sRGB color space,
/// but the texture views are in linear color space.
TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR,
};
TEX_COLOR_CONVERSION_MODE TexColorConversionMode = TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR;
};

enum ALPHA_MODE
Expand Down
5 changes: 5 additions & 0 deletions PBR/src/PBR_Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ void PBR_Renderer::CreatePSO(IRenderDevice* pDevice, IRenderStateCache* pStateCa
ShaderMacroHelper Macros;
Macros.AddShaderMacro("MAX_JOINT_COUNT", static_cast<int>(m_Settings.MaxJointCount));
Macros.AddShaderMacro("ALLOW_DEBUG_VIEW", m_Settings.AllowDebugView);
Macros.AddShaderMacro("CONVERT_OUTPUT_TO_SRGB", m_Settings.ConvertOutputToSRGB);
Macros.AddShaderMacro("TONE_MAPPING_MODE", "TONE_MAPPING_MODE_UNCHARTED2");
Macros.AddShaderMacro("PBR_USE_IBL", m_Settings.UseIBL);
Macros.AddShaderMacro("PBR_USE_AO", m_Settings.UseAO);
Expand All @@ -546,6 +547,10 @@ void PBR_Renderer::CreatePSO(IRenderDevice* pDevice, IRenderStateCache* pStateCa
Macros.AddShaderMacro("USE_HDR_IBL_CUBEMAPS", true);
Macros.AddShaderMacro("USE_SEPARATE_METALLIC_ROUGHNESS_TEXTURES", m_Settings.UseSeparateMetallicRoughnessTextures);

Macros.AddShaderMacro("TEX_COLOR_CONVERSION_MODE_NONE", CreateInfo::TEX_COLOR_CONVERSION_MODE_NONE);
Macros.AddShaderMacro("TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR", CreateInfo::TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR);
Macros.AddShaderMacro("TEX_COLOR_CONVERSION_MODE", m_Settings.TexColorConversionMode);

ShaderCI.Macros = Macros;
RefCntAutoPtr<IShader> pVS;
{
Expand Down
50 changes: 50 additions & 0 deletions Shaders/Common/public/SRGBUtilities.fxh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#ifndef _SRGB_UTILITIES_FXH_
#define _SRGB_UTILITIES_FXH_

float3 SRGBToLinear(float3 sRGB)
{
float3 bLess = step(float3(0.04045, 0.04045, 0.04045), sRGB);
return lerp(sRGB / 12.92,
pow(saturate((sRGB + float3(0.055, 0.055, 0.055)) / 1.055), float3(2.4, 2.4, 2.4)),
bLess);
}

float4 SRGBAToLinear(float4 sRGBA)
{
return float4(SRGBToLinear(sRGBA.rgb), sRGBA.a);
}

float3 FastSRGBToLinear(float3 sRGB)
{
return pow(sRGB, float3(2.2, 2.2, 2.2));
}

float4 FastSRGBAToLinear(float4 sRGBA)
{
return float4(FastSRGBToLinear(sRGBA.rgb), sRGBA.a);
}

float3 LinearToSRGB(float3 RGB)
{
float3 bGreater = step(float3(0.0031308, 0.0031308, 0.0031308), RGB);
return lerp(RGB * 12.92,
(pow(RGB, float3(1.0 / 2.4, 1.0 / 2.4, 1.0 / 2.4)) * 1.055) - float3(0.055, 0.055, 0.055),
bGreater);
}

float4 LinearToSRGBA(float4 RGBA)
{
return float4(LinearToSRGB(RGBA.rgb), RGBA.a);
}

float3 FastLinearToSRGB(float3 RGB)
{
return pow(RGB, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2));
}

float4 FastLinearToSRGBA(float4 RGBA)
{
return float4(FastLinearToSRGB(RGBA.rgb), RGBA.a);
}

#endif // _SRGB_UTILITIES_FXH_
43 changes: 23 additions & 20 deletions Shaders/PBR/private/RenderPBR.psh
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void main(in PbrVsOutput VSOut,
{
float4 BaseColor = SampleTexture(g_ColorMap, g_ColorMap_sampler, VSOut.UV0, VSOut.UV1, g_PBRAttribs.Material.BaseColorTextureUVSelector,
g_PBRAttribs.Material.BaseColorUVScaleBias, g_PBRAttribs.Material.BaseColorSlice, float4(1.0, 1.0, 1.0, 1.0));
BaseColor = SRGBtoLINEAR(BaseColor) * g_PBRAttribs.Material.BaseColorFactor;
BaseColor = float4(TO_LINEAR(BaseColor.rgb), BaseColor.a) * g_PBRAttribs.Material.BaseColorFactor;
//BaseColor *= getVertexColor();

float2 NormalMapUV = lerp(VSOut.UV0, VSOut.UV1, g_PBRAttribs.Material.NormalTextureUVSelector);
Expand Down Expand Up @@ -177,6 +177,7 @@ void main(in PbrVsOutput VSOut,
#if PBR_USE_EMISSIVE
Emissive = SampleTexture(g_EmissiveMap, g_EmissiveMap_sampler, VSOut.UV0, VSOut.UV1, g_PBRAttribs.Material.EmissiveTextureUVSelector,
g_PBRAttribs.Material.EmissiveUVScaleBias, g_PBRAttribs.Material.EmissiveSlice, float4(0.0, 0.0, 0.0, 0.0)).rgb;
Emissive = TO_LINEAR(Emissive);
#endif

float4 PhysicalDesc = float4(0.0, 0.0, 0.0, 0.0);
Expand All @@ -199,12 +200,13 @@ void main(in PbrVsOutput VSOut,
float metallic;
if (g_PBRAttribs.Material.Workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS)
{
PhysicalDesc.rgb = SRGBtoLINEAR(PhysicalDesc.rgb) * g_PBRAttribs.Material.SpecularFactor.rgb;
PhysicalDesc.rgb = TO_LINEAR(PhysicalDesc.rgb) * g_PBRAttribs.Material.SpecularFactor.rgb;
const float u_GlossinessFactor = 1.0;
PhysicalDesc.a *= u_GlossinessFactor;
}
else if(g_PBRAttribs.Material.Workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS)
{
// PhysicalDesc should already be in linear space
PhysicalDesc.g = saturate(PhysicalDesc.g * g_PBRAttribs.Material.RoughnessFactor);
PhysicalDesc.b = saturate(PhysicalDesc.b * g_PBRAttribs.Material.MetallicFactor);
}
Expand Down Expand Up @@ -257,8 +259,6 @@ void main(in PbrVsOutput VSOut,
#endif

#if PBR_USE_EMISSIVE
const float u_EmissiveFactor = 1.0;
Emissive = SRGBtoLINEAR(Emissive);
color += Emissive.rgb * g_PBRAttribs.Material.EmissiveFactor.rgb * g_PBRAttribs.Renderer.EmissionScale;
#endif

Expand All @@ -278,25 +278,28 @@ void main(in PbrVsOutput VSOut,
{
switch (g_PBRAttribs.Renderer.DebugViewType)
{
case 1: OutColor.rgba = BaseColor; break;
case 2: OutColor.rgba = float4(BaseColor.a, BaseColor.a, BaseColor.a, 1.0); break;
// Apply extra srgb->linear transform to make the maps look better
case 3: OutColor.rgb = SRGBtoLINEAR(TSNormal.xyz); break;
case 4: OutColor.rgb = SRGBtoLINEAR(Occlusion * float3(1.0, 1.0, 1.0)); break;
case 5: OutColor.rgb = SRGBtoLINEAR(Emissive.rgb); break;
case 6: OutColor.rgb = SRGBtoLINEAR(metallic * float3(1.0, 1.0, 1.0) ); break;
case 7: OutColor.rgb = SRGBtoLINEAR(SrfInfo.PerceptualRoughness * float3(1.0, 1.0, 1.0)); break;
case 8: OutColor.rgb = SrfInfo.DiffuseColor; break;
case 9: OutColor.rgb = SrfInfo.Reflectance0; break;
case 10: OutColor.rgb = SrfInfo.Reflectance90; break;
case 11: OutColor.rgb = SRGBtoLINEAR(abs(VSOut.Normal / max(length(VSOut.Normal), 1e-3))); break;
case 12: OutColor.rgb = SRGBtoLINEAR(abs(perturbedNormal)); break;
case 13: OutColor.rgb = dot(perturbedNormal, view) * float3(1.0, 1.0, 1.0); break;
case 1: OutColor.rgba = BaseColor; break;
case 2: OutColor.rgba = float4(BaseColor.a, BaseColor.a, BaseColor.a, 1.0); break;
case 3: OutColor.rgb = TSNormal.xyz; break;
case 4: OutColor.rgb = Occlusion * float3(1.0, 1.0, 1.0); break;
case 5: OutColor.rgb = Emissive.rgb; break;
case 6: OutColor.rgb = metallic * float3(1.0, 1.0, 1.0); break;
case 7: OutColor.rgb = SrfInfo.PerceptualRoughness * float3(1.0, 1.0, 1.0); break;
case 8: OutColor.rgb = SrfInfo.DiffuseColor; break;
case 9: OutColor.rgb = SrfInfo.Reflectance0; break;
case 10: OutColor.rgb = SrfInfo.Reflectance90; break;
case 11: OutColor.rgb = abs(VSOut.Normal / max(length(VSOut.Normal), 1e-3)); break;
case 12: OutColor.rgb = abs(perturbedNormal); break;
case 13: OutColor.rgb = dot(perturbedNormal, view) * float3(1.0, 1.0, 1.0); break;
#if PBR_USE_IBL
case 14: OutColor.rgb = IBLContrib.f3Diffuse; break;
case 15: OutColor.rgb = IBLContrib.f3Specular; break;
case 14: OutColor.rgb = IBLContrib.f3Diffuse; break;
case 15: OutColor.rgb = IBLContrib.f3Specular; break;
#endif
}
}
#endif

#if CONVERT_OUTPUT_TO_SRGB
OutColor.rgb = FastLinearToSRGB(OutColor.rgb);
#endif
}
45 changes: 17 additions & 28 deletions Shaders/PBR/public/PBR_Shading.fxh
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@
#include "PBR_Structures.fxh"
#include "PBR_Common.fxh"
#include "ShaderUtilities.fxh"
#include "SRGBUtilities.fxh"

#ifndef MANUAL_SRGB_TO_LINEAR
# define MANUAL_SRGB_TO_LINEAR 1
#ifndef TEX_COLOR_CONVERSION_MODE_NONE
# define TEX_COLOR_CONVERSION_MODE_NONE 0
#endif

#ifndef SRGB_FAST_APPROXIMATION
# define SRGB_FAST_APPROXIMATION 1
#ifndef TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR
# define TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR 1
#endif

#ifndef TEX_COLOR_CONVERSION_MODE
# define TEX_COLOR_CONVERSION_MODE TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR
#endif

#if TEX_COLOR_CONVERSION_MODE == TEX_COLOR_CONVERSION_MODE_NONE
# define TO_LINEAR(x) (x)
#elif TEX_COLOR_CONVERSION_MODE == TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR
# define TO_LINEAR FastSRGBToLinear
#endif

#ifndef USE_IBL_ENV_MAP_LOD
Expand Down Expand Up @@ -48,28 +59,6 @@ float SolveMetallic(float3 diffuse,
return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
}


float3 SRGBtoLINEAR(float3 srgbIn)
{
#if MANUAL_SRGB_TO_LINEAR
# if SRGB_FAST_APPROXIMATION
float3 linOut = pow(saturate(srgbIn.xyz), float3(2.2, 2.2, 2.2));
# else
float3 bLess = step(float3(0.04045, 0.04045, 0.04045), srgbIn.xyz);
float3 linOut = mix( srgbIn.xyz/12.92, pow(saturate((srgbIn.xyz + float3(0.055, 0.055, 0.055)) / 1.055), float3(2.4, 2.4, 2.4)), bLess );
# endif
return linOut;
#else
return srgbIn;
#endif
}

float4 SRGBtoLINEAR(float4 srgbIn)
{
return float4(SRGBtoLINEAR(srgbIn.xyz), srgbIn.w);
}


float3 ApplyDirectionalLight(float3 lightDir, float3 lightColor, SurfaceReflectanceInfo srfInfo, float3 normal, float3 view)
{
float3 pointToLight = -lightDir;
Expand Down Expand Up @@ -162,8 +151,8 @@ IBL_Contribution GetIBLContribution(in SurfaceReflectanceInfo SrfInfo,
float3 diffuseLight = diffuseSample.rgb;
float3 specularLight = specularSample.rgb;
#else
float3 diffuseLight = SRGBtoLINEAR(diffuseSample).rgb;
float3 specularLight = SRGBtoLINEAR(specularSample).rgb;
float3 diffuseLight = TO_LINEAR(diffuseSample.rgb);
float3 specularLight = TO_LINEAR(specularSample.rgb);
#endif

IBL_Contribution IBLContrib;
Expand Down
45 changes: 17 additions & 28 deletions shaders_inc/PBR_Shading.fxh.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@
"#include \"PBR_Structures.fxh\"\n"
"#include \"PBR_Common.fxh\"\n"
"#include \"ShaderUtilities.fxh\"\n"
"#include \"SRGBUtilities.fxh\"\n"
"\n"
"#ifndef MANUAL_SRGB_TO_LINEAR\n"
"# define MANUAL_SRGB_TO_LINEAR 1\n"
"#ifndef TEX_COLOR_CONVERSION_MODE_NONE\n"
"# define TEX_COLOR_CONVERSION_MODE_NONE 0\n"
"#endif\n"
"\n"
"#ifndef SRGB_FAST_APPROXIMATION\n"
"# define SRGB_FAST_APPROXIMATION 1\n"
"#ifndef TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR\n"
"# define TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR 1\n"
"#endif\n"
"\n"
"#ifndef TEX_COLOR_CONVERSION_MODE\n"
"# define TEX_COLOR_CONVERSION_MODE TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR\n"
"#endif\n"
"\n"
"#if TEX_COLOR_CONVERSION_MODE == TEX_COLOR_CONVERSION_MODE_NONE\n"
"# define TO_LINEAR(x) (x)\n"
"#elif TEX_COLOR_CONVERSION_MODE == TEX_COLOR_CONVERSION_MODE_SRGB_TO_LINEAR\n"
"# define TO_LINEAR FastSRGBToLinear\n"
"#endif\n"
"\n"
"#ifndef USE_IBL_ENV_MAP_LOD\n"
Expand Down Expand Up @@ -48,28 +59,6 @@
" return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);\n"
"}\n"
"\n"
"\n"
"float3 SRGBtoLINEAR(float3 srgbIn)\n"
"{\n"
"#if MANUAL_SRGB_TO_LINEAR\n"
"# if SRGB_FAST_APPROXIMATION\n"
" float3 linOut = pow(saturate(srgbIn.xyz), float3(2.2, 2.2, 2.2));\n"
"# else\n"
" float3 bLess = step(float3(0.04045, 0.04045, 0.04045), srgbIn.xyz);\n"
" float3 linOut = mix( srgbIn.xyz/12.92, pow(saturate((srgbIn.xyz + float3(0.055, 0.055, 0.055)) / 1.055), float3(2.4, 2.4, 2.4)), bLess );\n"
"# endif\n"
" return linOut;\n"
"#else\n"
" return srgbIn;\n"
"#endif\n"
"}\n"
"\n"
"float4 SRGBtoLINEAR(float4 srgbIn)\n"
"{\n"
" return float4(SRGBtoLINEAR(srgbIn.xyz), srgbIn.w);\n"
"}\n"
"\n"
"\n"
"float3 ApplyDirectionalLight(float3 lightDir, float3 lightColor, SurfaceReflectanceInfo srfInfo, float3 normal, float3 view)\n"
"{\n"
" float3 pointToLight = -lightDir;\n"
Expand Down Expand Up @@ -162,8 +151,8 @@
" float3 diffuseLight = diffuseSample.rgb;\n"
" float3 specularLight = specularSample.rgb;\n"
"#else\n"
" float3 diffuseLight = SRGBtoLINEAR(diffuseSample).rgb;\n"
" float3 specularLight = SRGBtoLINEAR(specularSample).rgb;\n"
" float3 diffuseLight = TO_LINEAR(diffuseSample.rgb);\n"
" float3 specularLight = TO_LINEAR(specularSample.rgb);\n"
"#endif\n"
"\n"
" IBL_Contribution IBLContrib;\n"
Expand Down
Loading

0 comments on commit 8e70179

Please sign in to comment.