Commit 38068be1 authored by Rye Mutt's avatar Rye Mutt 🍞
Browse files

Clean up tonemapping impl and add support for tweaking parameters

parent 7649bb5c
......@@ -32,11 +32,34 @@
#include "llvertexbuffer.h"
#include "alcontrolcache.h"
#include "llviewercontrol.h"
#include "llviewershadermgr.h"
#include "pipeline.h"
const U32 ALRENDER_BUFFER_MASK = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1;
static LLStaticHashedString al_exposure("exposure");
static LLStaticHashedString tone_uchimura_a("tone_uchimura_a");
static LLStaticHashedString tone_uchimura_b("tone_uchimura_b");
static LLStaticHashedString tone_lottes_a("tone_lottes_a");
static LLStaticHashedString tone_lottes_b("tone_lottes_b");
static LLStaticHashedString tone_uncharted_a("tone_uncharted_a");
static LLStaticHashedString tone_uncharted_b("tone_uncharted_b");
static LLStaticHashedString tone_uncharted_c("tone_uncharted_c");
ALRenderUtil::ALRenderUtil()
{
// Connect settings
gSavedSettings.getControl("RenderToneMapType")->getSignal()->connect(boost::bind(&ALRenderUtil::setupTonemap, this));
gSavedSettings.getControl("RenderToneMapExposure")->getSignal()->connect(boost::bind(&ALRenderUtil::setupTonemap, this));
gSavedSettings.getControl("RenderToneMapLottesA")->getSignal()->connect(boost::bind(&ALRenderUtil::setupTonemap, this));
gSavedSettings.getControl("RenderToneMapLottesB")->getSignal()->connect(boost::bind(&ALRenderUtil::setupTonemap, this));
gSavedSettings.getControl("RenderToneMapUchimuraA")->getSignal()->connect(boost::bind(&ALRenderUtil::setupTonemap, this));
gSavedSettings.getControl("RenderToneMapUchimuraB")->getSignal()->connect(boost::bind(&ALRenderUtil::setupTonemap, this));
// Shader setup
refreshState();
}
void ALRenderUtil::restoreVertexBuffers()
{
......@@ -62,6 +85,39 @@ void ALRenderUtil::resetVertexBuffers()
mRenderBuffer = nullptr;
}
void ALRenderUtil::refreshState()
{
setupTonemap();
}
bool ALRenderUtil::setupTonemap()
{
if (LLPipeline::sRenderDeferred)
{
mTonemapType = gSavedSettings.getU32("RenderToneMapType");
if (mTonemapType >= TONEMAP_COUNT)
{
mTonemapType = ALTonemap::NONE;
}
mTonemapExposure = llclamp(gSavedSettings.getF32("RenderToneMapExposure"), 0.1f, 16.f);
mToneLottesParamA = gSavedSettings.getVector3("RenderToneMapLottesA");
mToneLottesParamB = gSavedSettings.getVector3("RenderToneMapLottesB");
mToneUchimuraParamA = gSavedSettings.getVector3("RenderToneMapUchimuraA");
mToneUchimuraParamB = gSavedSettings.getVector3("RenderToneMapUchimuraB");
mToneUnchartedParamA = gSavedSettings.getVector3("RenderToneMapUnchartedA");
mToneUnchartedParamB = gSavedSettings.getVector3("RenderToneMapUnchartedB");
mToneUnchartedParamC = gSavedSettings.getVector3("RenderToneMapUnchartedC");
}
else
{
mTonemapType = ALTonemap::NONE;
mTonemapExposure = 1.f;
}
return true;
}
void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
{
gGL.matrixMode(LLRender::MM_PROJECTION);
......@@ -74,58 +130,49 @@ void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
LLGLDepthTest depth(GL_FALSE, GL_FALSE);
dst->bindTarget();
LLGLSLShader* shader;
switch (ALControlCache::RenderToneMapType)
LLGLSLShader* tone_shader = &gDeferredPostTonemapProgram[mTonemapType];
tone_shader->bind();
S32 channel = tone_shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, src->getUsage());
if (channel > -1)
{
src->bindTexture(0, channel, LLTexUnit::TFO_POINT);
}
tone_shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, dst->getWidth(), dst->getHeight());
tone_shader->uniform1f(al_exposure, mTonemapExposure);
switch (mTonemapType)
{
default:
case ALTonemap::NONE:
shader = &gPostTonemapProgram[NONE];
break;
case ALTonemap::LINEAR:
shader = &gPostTonemapProgram[LINEAR];
break;
case ALTonemap::REINHARD:
shader = &gPostTonemapProgram[REINHARD];
break;
case ALTonemap::REINHARD2:
shader = &gPostTonemapProgram[REINHARD2];
break;
case ALTonemap::FILMIC:
shader = &gPostTonemapProgram[FILMIC];
break;
case ALTonemap::UNREAL:
shader = &gPostTonemapProgram[UNREAL];
break;
case ALTonemap::ACES:
shader = &gPostTonemapProgram[ACES];
break;
case ALTonemap::UCHIMURA:
shader = &gPostTonemapProgram[UCHIMURA];
{
tone_shader->uniform3fv(tone_uchimura_a, 1, mToneUchimuraParamA.mV);
tone_shader->uniform3fv(tone_uchimura_b, 1, mToneUchimuraParamB.mV);
break;
}
case ALTonemap::LOTTES:
shader = &gPostTonemapProgram[LOTTES];
{
tone_shader->uniform3fv(tone_lottes_a, 1, mToneLottesParamA.mV);
tone_shader->uniform3fv(tone_lottes_b, 1, mToneLottesParamB.mV);
break;
}
shader->bind();
S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, src->getUsage());
if (channel > -1)
case ALTonemap::UNCHARTED:
{
src->bindTexture(0, channel, LLTexUnit::TFO_POINT);
tone_shader->uniform3fv(tone_uncharted_a, 1, mToneUnchartedParamA.mV);
tone_shader->uniform3fv(tone_uncharted_b, 1, mToneUnchartedParamB.mV);
tone_shader->uniform3fv(tone_uncharted_c, 1, mToneUnchartedParamC.mV);
break;
}
}
shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, dst->getWidth(), dst->getHeight());
F32 exposure = llclamp(ALControlCache::RenderToneMapExposure, 0.1f, 8.f);
shader->uniform1f(al_exposure, exposure);
mRenderBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX);
mRenderBuffer->drawArrays(LLRender::TRIANGLES, 0, 3);
stop_glerror();
shader->unbind();
tone_shader->unbind();
dst->flush();
gGL.matrixMode(LLRender::MM_PROJECTION);
......
......@@ -28,7 +28,7 @@
#include "llpointer.h"
#define AL_TONEMAP_COUNT 9
#define AL_TONEMAP_COUNT 10
class LLRenderTarget;
class LLVertexBuffer;
......@@ -36,12 +36,15 @@ class LLVertexBuffer;
class ALRenderUtil
{
public:
ALRenderUtil() = default;
ALRenderUtil();
~ALRenderUtil() = default;
void restoreVertexBuffers();
void resetVertexBuffers();
void refreshState();
// Deferred Only Functions
enum ALTonemap : uint32_t
{
NONE = 0,
......@@ -53,11 +56,27 @@ class ALRenderUtil
ACES,
UCHIMURA,
LOTTES,
UNCHARTED,
TONEMAP_COUNT
};
bool setupTonemap();
void renderTonemap(LLRenderTarget* src, LLRenderTarget* dst);
// End Deferred Only
private:
// Parameters
F32 mTonemapExposure = 1.f;
// State
U32 mTonemapType = ALTonemap::NONE;
LLVector3 mToneLottesParamA;
LLVector3 mToneLottesParamB;
LLVector3 mToneUchimuraParamA;
LLVector3 mToneUchimuraParamB;
LLVector3 mToneUnchartedParamA;
LLVector3 mToneUnchartedParamB;
LLVector3 mToneUnchartedParamC;
// Vertex Buffers
LLPointer<LLVertexBuffer> mRenderBuffer;
};
......@@ -695,10 +695,51 @@
<key>Value</key>
<real>0.275</real>
</map>
<key>RenderToneMapExposure</key>
<map>
<key>Comment</key>
<string>Tonemapping exposure multiplier</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>1.0</real>
</map>
<key>RenderToneMapLottesA</key>
<map>
<key>Comment</key>
<string>x (a), y (d), z (hdrMax)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>1.6</real>
<real>0.977</real>
<real>8.0</real>
</array>
</map>
<key>RenderToneMapLottesB</key>
<map>
<key>Comment</key>
<string>x (midIn), y (black), z (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>0.18</real>
<real>0.267</real>
<real>0.0</real>
</array>
</map>
<key>RenderToneMapType</key>
<map>
<key>Comment</key>
<string>Tonemapping type 0 - None, 1 - Linear, 2 - Reinhard, 3 - Reinhard2, 4 - Filmic, 5 - Unreal, 6 - ACES, 7 - Uchimura, 8 - Lottes</string>
<string>Tonemapping type 0 - None, 1 - Linear, 2 - Reinhard, 3 - Reinhard2, 4 - Filmic, 5 - Unreal, 6 - ACES, 7 - Uchimura, 8 - Lottes, 9 - Uncharted</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
......@@ -706,16 +747,80 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderToneMapExposure</key>
<key>RenderToneMapUchimuraA</key>
<map>
<key>Comment</key>
<string>Tonemapping exposure multiplier</string>
<string>x (max display brightness), y (contrast), z (linear section start)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<string>Vector3</string>
<key>Value</key>
<real>1.0</real>
<array>
<real>1.0</real>
<real>1.0</real>
<real>0.22</real>
</array>
</map>
<key>RenderToneMapUchimuraB</key>
<map>
<key>Comment</key>
<string>x (linear section length), y (black), z (pedestal)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>0.4</real>
<real>1.33</real>
<real>0.0</real>
</array>
</map>
<key>RenderToneMapUnchartedA</key>
<map>
<key>Comment</key>
<string>A, B, C</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>0.15</real>
<real>0.50</real>
<real>0.10</real>
</array>
</map>
<key>RenderToneMapUnchartedB</key>
<map>
<key>Comment</key>
<string>D, E, F</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>0.20</real>
<real>0.02</real>
<real>0.20</real>
</array>
</map>
<key>RenderToneMapUnchartedC</key>
<map>
<key>Comment</key>
<string>W, ExposureBias, UNUSED</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>11.2</real>
<real>2.0</real>
<real>0.0</real>
</array>
</map>
<key>ResetUserColorsOnLogout</key>
<map>
......
......@@ -34,7 +34,7 @@ out vec4 frag_color;
#define frag_color gl_FragColor
#endif
VARYING vec2 vary_texcoord0;
VARYING vec2 vary_fragcoord;
uniform sampler2D tex0;
......@@ -65,18 +65,18 @@ void main()
// a b c
// d(e)f
// g h i
vec4 inputColor = texture2DLod(tex0, vary_texcoord0, 0.0f);
vec4 inputColor = texture2DLod(tex0, vary_fragcoord, 0.0f);
float alpha = inputColor.a;
vec3 a = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2(-1,-1)).rgb;
vec3 b = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2( 0,-1)).rgb;
vec3 c = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2( 1,-1)).rgb;
vec3 d = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2(-1, 0)).rgb;
vec3 a = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2(-1,-1)).rgb;
vec3 b = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2( 0,-1)).rgb;
vec3 c = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2( 1,-1)).rgb;
vec3 d = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2(-1, 0)).rgb;
vec3 e = inputColor.rgb;
vec3 f = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2( 1, 0)).rgb;
vec3 g = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2(-1, 1)).rgb;
vec3 h = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2( 0, 1)).rgb;
vec3 i = texture2DLodOffset(tex0, vary_texcoord0, 0.0f, ivec2( 1, 1)).rgb;
vec3 f = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2( 1, 0)).rgb;
vec3 g = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2(-1, 1)).rgb;
vec3 h = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2( 0, 1)).rgb;
vec3 i = texture2DLodOffset(tex0, vary_fragcoord, 0.0f, ivec2( 1, 1)).rgb;
// Soft min and max.
// a b c b
......
......@@ -56,14 +56,16 @@ vec3 filmic(vec3 color)
return color;
}
vec3 unreal(vec3 x) {
vec3 unreal(vec3 x)
{
// Unreal 3, Documentation: "Color Grading"
// Adapted to be close to Tonemap_ACES, with similar range
// Gamma 2.2 correction is baked in, don't use with sRGB conversion!
return x / (x + 0.155) * 1.019;
}
vec3 aces(vec3 x) {
vec3 aces(vec3 x)
{
// Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
const float a = 2.51;
const float b = 0.03;
......@@ -73,7 +75,9 @@ vec3 aces(vec3 x) {
return (x * (a * x + b)) / (x * (c * x + d) + e);
}
vec3 uchimura(vec3 x, float P, float a, float m, float l, float c, float b) {
#if TONEMAP_METHOD == 7 // Uchimura
vec3 uchimura(vec3 x, float P, float a, float m, float l, float c, float b)
{
float l0 = ((P - m) * l) / a;
float L0 = m - m / a;
float L1 = m + (1.0 - m) / a;
......@@ -93,34 +97,70 @@ vec3 uchimura(vec3 x, float P, float a, float m, float l, float c, float b) {
return T * w0 + L * w1 + S * w2;
}
vec3 uchimura(vec3 x) {
const float P = 1.0; // max display brightness
const float a = 1.0; // contrast
const float m = 0.22; // linear section start
const float l = 0.4; // linear section length
const float c = 1.33; // black
const float b = 0.0; // pedestal
return uchimura(x, P, a, m, l, c, b);
uniform vec3 tone_uchimura_a = vec3(1.0, 1.0, 0.22);
uniform vec3 tone_uchimura_b = vec3(0.4, 1.33, 0.0);
vec3 uchimura(vec3 x)
{
float P = tone_uchimura_a.x; // max display brightness
float a = tone_uchimura_a.y; // contrast
float m = tone_uchimura_a.z; // linear section start
float l = tone_uchimura_b.x; // linear section length
float c = tone_uchimura_b.y; // black
float b = tone_uchimura_b.z; // pedestal
return uchimura(x, P, a, m, l, c, b);
}
#endif
#if TONEMAP_METHOD == 8
// Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines"
vec3 lottes(vec3 x) {
const vec3 a = vec3(1.6);
const vec3 d = vec3(0.977);
const vec3 hdrMax = vec3(8.0);
const vec3 midIn = vec3(0.18);
const vec3 midOut = vec3(0.267);
const vec3 b =
uniform vec3 tone_lottes_a = vec3(1.6, 0.977, 8.0);
uniform vec3 tone_lottes_b = vec3(0.18, 0.267, 0.0);
vec3 lottes(vec3 x)
{
vec3 a = vec3(tone_lottes_a.x);
vec3 d = vec3(tone_lottes_a.y);
vec3 hdrMax = vec3(tone_lottes_a.z);
vec3 midIn = vec3(tone_lottes_b.x);
vec3 midOut = vec3(tone_lottes_b.y);
vec3 b =
(-pow(midIn, a) + pow(hdrMax, a) * midOut) /
((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
const vec3 c =
vec3 c =
(pow(hdrMax, a * d) * pow(midIn, a) - pow(hdrMax, a) * pow(midIn, a * d) * midOut) /
((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
return pow(x, a) / (pow(x, a * d) * b + c);
}
#endif
#if TONEMAP_METHOD == 9
// Hable, http://filmicworlds.com/blog/filmic-tonemapping-operators/
uniform vec3 tone_uncharted_a = vec3(0.15, 0.50, 0.10); // A, B, C
uniform vec3 tone_uncharted_b = vec3(0.20, 0.02, 0.30); // D, E, F
uniform vec3 tone_uncharted_c = vec3(11.2, 2.0, 0.0); // W, ExposureBias, Unused
vec3 Uncharted2Tonemap(vec3 x)
{
float A = tone_uncharted_a.x;
float B = tone_uncharted_a.y;
float C = tone_uncharted_a.z;
float D = tone_uncharted_b.x;
float E = tone_uncharted_b.y;
float F = tone_uncharted_b.z;
return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}
vec3 uncharted2(vec3 col)
{
float ExposureBias = tone_uncharted_c.y;
vec3 curr = Uncharted2Tonemap(ExposureBias*col);
vec3 whiteScale = vec3(1.0f)/Uncharted2Tonemap(vec3(tone_uncharted_c.x));
return curr*whiteScale;
}
#endif
void main()
{
......@@ -154,6 +194,9 @@ void main()
#elif TONEMAP_METHOD == 8 // Lottes 2016
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = lottes(diff.rgb);
#elif TONEMAP_METHOD == 9 // Uncharted
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = uncharted2(diff.rgb);
#else
#define NEEDS_GAMMA_CORRECT 1
#endif
......
/**
* @file SRGBToLinearF.glsl
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2021, Rye Mutt<rye@alchemyviewer.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
/*[EXTRA_CODE_HERE]*/
#ifdef DEFINE_GL_FRAGCOLOR
out vec4 frag_color;
#else
#define frag_color gl_FragColor
#endif
VARYING vec2 vary_texcoord0;
uniform sampler2D tex0;
vec3 srgb_to_linear(vec3 cs);
void main()
{
vec4 diff = texture2D(tex0, vary_texcoord0);
diff.rgb = srgb_to_linear(diff.rgb);
frag_color = diff;
}
/**
* @file linearToSRGBF.glsl
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2021, Rye Mutt<rye@alchemyviewer.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
/*[EXTRA_CODE_HERE]*/