Commit 059fd356 authored by Rye Mutt's avatar Rye Mutt 🍞
Browse files

Initial tonemapping support

parent 471a56b6
......@@ -129,6 +129,7 @@ set(viewer_SOURCE_FILES
alpanelmusicticker.cpp
alpanelquicksettings.cpp
alpanelquicksettingspulldown.cpp
alrenderutils.cpp
altoolalign.cpp
alunzip.cpp
alviewermenu.cpp
......@@ -800,6 +801,7 @@ set(viewer_HEADER_FILES
alpanelmusicticker.h
alpanelquicksettings.h
alpanelquicksettingspulldown.h
alrenderutils.h
altoolalign.h
alunzip.h
alviewermenu.h
......
......@@ -27,6 +27,8 @@ bool ALControlCache::SnapEnabled;
S32 ALControlCache::ToastGap;
F32 ALControlCache::YawFromMousePosition = 90.f;
F32 ALControlCache::PitchFromMousePosition = 90.f;
U32 ALControlCache::RenderToneMapType = 0;
F32 ALControlCache::RenderToneMapExposure = 1.f;
#define DECLARE_CTRL(ctrl, type, ctrl_type) \
......@@ -80,4 +82,7 @@ void ALControlCache::initControls()
DECLARE_CTRL_S32(ToastGap);
DECLARE_CTRL_F32(YawFromMousePosition);
DECLARE_CTRL_F32(PitchFromMousePosition);
DECLARE_CTRL_U32(RenderToneMapType);
DECLARE_CTRL_F32(RenderToneMapExposure);
}
......@@ -29,6 +29,8 @@ struct ALControlCache
static S32 ToastGap;
static F32 YawFromMousePosition;
static F32 PitchFromMousePosition;
static U32 RenderToneMapType;
static F32 RenderToneMapExposure;
};
#endif
\ No newline at end of file
/**
* @file alrenderutils.cpp
* @brief Alchemy Render Utility
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Alchemy Viewer Source Code
* Copyright (C) 2021, Alchemy Viewer Project.
* 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
*
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "alrenderutils.h"
#include "llrendertarget.h"
#include "llvertexbuffer.h"
#include "alcontrolcache.h"
#include "llviewershadermgr.h"
const U32 ALRENDER_BUFFER_MASK = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1;
static LLStaticHashedString al_exposure("exposure");
void ALRenderUtil::restoreVertexBuffers()
{
mRenderBuffer = new LLVertexBuffer(ALRENDER_BUFFER_MASK, 0);
mRenderBuffer->allocateBuffer(3, 0, true);
LLStrider<LLVector3> vert;
LLStrider<LLVector2> tc0;
LLStrider<LLVector2> tc1;
mRenderBuffer->getVertexStrider(vert);
mRenderBuffer->getTexCoord0Strider(tc0);
mRenderBuffer->getTexCoord1Strider(tc1);
vert[0].set(-1.f, -1.f, 0.f);
vert[1].set(3.f, -1.f, 0.f);
vert[2].set(-1.f, 3.f, 0.f);
mRenderBuffer->flush();
}
void ALRenderUtil::resetVertexBuffers()
{
mRenderBuffer = nullptr;
}
void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
{
gGL.matrixMode(LLRender::MM_PROJECTION);
gGL.pushMatrix();
gGL.loadIdentity();
gGL.matrixMode(LLRender::MM_MODELVIEW);
gGL.pushMatrix();
gGL.loadIdentity();
LLGLDepthTest depth(GL_FALSE, GL_FALSE);
dst->bindTarget();
LLGLSLShader* shader;
switch (ALControlCache::RenderToneMapType)
{
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];
break;
case ALTonemap::LOTTES:
shader = &gPostTonemapProgram[LOTTES];
break;
}
shader->bind();
S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, src->getUsage());
if (channel > -1)
{
src->bindTexture(0, channel, LLTexUnit::TFO_POINT);
}
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();
dst->flush();
gGL.matrixMode(LLRender::MM_PROJECTION);
gGL.popMatrix();
gGL.matrixMode(LLRender::MM_MODELVIEW);
gGL.popMatrix();
}
\ No newline at end of file
/**
* @file alrenderutils.h
* @brief Alchemy Render Utility
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Alchemy Viewer Source Code
* Copyright (C) 2021, Alchemy Viewer Project.
* 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
*
* $/LicenseInfo$
*/
#pragma once
#include "llpointer.h"
#define AL_TONEMAP_COUNT 9
class LLRenderTarget;
class LLVertexBuffer;
class ALRenderUtil
{
public:
ALRenderUtil() = default;
~ALRenderUtil() = default;
void restoreVertexBuffers();
void resetVertexBuffers();
enum ALTonemap : uint32_t
{
NONE = 0,
LINEAR,
REINHARD,
REINHARD2,
FILMIC,
UNREAL,
ACES,
UCHIMURA,
LOTTES,
TONEMAP_COUNT
};
void renderTonemap(LLRenderTarget* src, LLRenderTarget* dst);
private:
LLPointer<LLVertexBuffer> mRenderBuffer;
};
......@@ -684,6 +684,28 @@
<key>Value</key>
<real>0.275</real>
</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>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>0</integer>
</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>ResetUserColorsOnLogout</key>
<map>
<key>Comment</key>
......
/**
* @file postNoTCV.glsl
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Alchemy 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
*
* $/LicenseInfo$
*/
uniform mat4 modelview_projection_matrix;
ATTRIBUTE vec3 position;
VARYING vec2 vary_fragcoord;
uniform vec2 screen_res = vec2(1.f, 1.f);
void main()
{
//transform vertex
vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0);
gl_Position = pos;
vary_fragcoord = (pos.xy*0.5+0.5) * screen_res;
}
/**
* @file toneMapF.glsl
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Alchemy 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
*
* $/LicenseInfo$
*/
/*[EXTRA_CODE_HERE]*/
#ifdef DEFINE_GL_FRAGCOLOR
out vec4 frag_color;
#else
#define frag_color gl_FragColor
#endif
VARYING vec2 vary_fragcoord;
uniform sampler2DRect diffuseRect;
uniform vec2 screen_res;
uniform float exposure;
vec3 linear_to_srgb(vec3 cl);
vec3 reinhard(vec3 x)
{
return x/(1+x);
}
vec3 reinhard2(vec3 x) {
const float L_white = 4.0;
return (x * (1.0 + x / (L_white * L_white))) / (1.0 + x);
}
vec3 filmic(vec3 color)
{
color = max(vec3(0.), color - vec3(0.004));
color = (color * (6.2 * color + .5)) / (color * (6.2 * color + 1.7) + 0.06);
return color;
}
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) {
// Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
const float a = 2.51;
const float b = 0.03;
const float c = 2.43;
const float d = 0.59;
const float e = 0.14;
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) {
float l0 = ((P - m) * l) / a;
float L0 = m - m / a;
float L1 = m + (1.0 - m) / a;
float S0 = m + l0;
float S1 = m + a * l0;
float C2 = (a * P) / (P - S1);
float CP = -C2 / P;
vec3 w0 = vec3(1.0 - smoothstep(0.0, m, x));
vec3 w2 = vec3(step(m + l0, x));
vec3 w1 = vec3(1.0 - w0 - w2);
vec3 T = vec3(m * pow(x / m, vec3(c)) + b);
vec3 S = vec3(P - (P - S1) * exp(CP * (x - S0)));
vec3 L = vec3(m + a * (x - m));
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);
}
// 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 =
(-pow(midIn, a) + pow(hdrMax, a) * midOut) /
((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
const 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);
}
void main()
{
vec4 diff = texture2DRect(diffuseRect, vary_fragcoord);
#if TONEMAP_METHOD != 0
// Exposure adjustment
diff.rgb *= exposure;
#endif
#if TONEMAP_METHOD == 0 // None, Gamma Correct Only
#define NEEDS_GAMMA_CORRECT 1
#elif TONEMAP_METHOD == 1 // Linear
#define NEEDS_GAMMA_CORRECT 1
#elif TONEMAP_METHOD == 2 // Reinhard method
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = reinhard(diff.rgb);
#elif TONEMAP_METHOD == 3 // Reinhard2 method
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = reinhard2(diff.rgb);
#elif TONEMAP_METHOD == 4 // Filmic method
diff.rgb = filmic(diff.rgb);
#elif TONEMAP_METHOD == 5 // Unreal method
diff.rgb = unreal(diff.rgb);
#elif TONEMAP_METHOD == 6 // Aces method
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = aces(diff.rgb);
#elif TONEMAP_METHOD == 7 // Uchimura's Gran Turismo method
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = uchimura(diff.rgb);
#elif TONEMAP_METHOD == 8 // Lottes 2016
#define NEEDS_GAMMA_CORRECT 1
diff.rgb = lottes(diff.rgb);
#else
#define NEEDS_GAMMA_CORRECT 1
#endif
#if NEEDS_GAMMA_CORRECT
diff.rgb = linear_to_srgb(diff.rgb);
#endif
frag_color = diff;
}
......@@ -179,6 +179,7 @@ LLGLSLShader gPostNightVisionProgram;
LLGLSLShader gPostSRGBToLinearProgram;
LLGLSLShader gPostLinearToSRGBProgram;
LLGLSLShader gPostCASProgram;
LLGLSLShader gPostTonemapProgram[AL_TONEMAP_COUNT];
LLGLSLShader gPostSMAAEdgeDetect[4];
LLGLSLShader gPostSMAABlendWeights[4];
LLGLSLShader gPostSMAANeighborhoodBlend[4];
......@@ -824,6 +825,10 @@ void LLViewerShaderMgr::unloadShaders()
gPostSRGBToLinearProgram.unload();
gPostLinearToSRGBProgram.unload();
gPostCASProgram.unload();
for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
{
gPostTonemapProgram[i].unload();
}
gDeferredDiffuseProgram.unload();
gDeferredDiffuseAlphaMaskProgram.unload();
......@@ -1172,6 +1177,10 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
gPostSRGBToLinearProgram.unload();
gPostLinearToSRGBProgram.unload();
gPostCASProgram.unload();
for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
{
gPostTonemapProgram[i].unload();
}
return TRUE;
}
......@@ -1236,6 +1245,24 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
success = gPostCASProgram.createShader(NULL, NULL);
}
for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
{
if (success)
{
gPostTonemapProgram[i].mName = "Tonemapping Shader " + std::to_string(i);
gPostTonemapProgram[i].mFeatures.hasSrgb = true;
gPostTonemapProgram[i].mShaderFiles.clear();
gPostTonemapProgram[i].mShaderFiles.push_back(make_pair("alchemy/postNoTCV.glsl", GL_VERTEX_SHADER));
gPostTonemapProgram[i].mShaderFiles.push_back(make_pair("alchemy/toneMapF.glsl", GL_FRAGMENT_SHADER));
gPostTonemapProgram[i].mShaderLevel = mShaderLevel[SHADER_EFFECT];
gPostTonemapProgram[i].clearPermutations();
gPostTonemapProgram[i].addPermutation("TONEMAP_METHOD", std::to_string(i));
success = gPostTonemapProgram[i].createShader(NULL, NULL);
}
}
return success;
}
......
......@@ -30,6 +30,8 @@
#include "llshadermgr.h"
#include "llmaterial.h"
#include "alrenderutils.h"
#define LL_DEFERRED_MULTI_LIGHT_COUNT 16
class LLViewerShaderMgr: public LLShaderMgr
......@@ -262,6 +264,8 @@ extern LLGLSLShader gPostNightVisionProgram;
extern LLGLSLShader gPostSRGBToLinearProgram;
extern LLGLSLShader gPostLinearToSRGBProgram;
extern LLGLSLShader gPostCASProgram;
extern LLGLSLShader gPostTonemapProgram[AL_TONEMAP_COUNT];
extern LLGLSLShader gPostSMAAEdgeDetect[4];
extern LLGLSLShader gPostSMAABlendWeights[4];
extern LLGLSLShader gPostSMAANeighborhoodBlend[4];
......
......@@ -465,6 +465,8 @@ void LLPipeline::init()
{
refreshCachedSettings();
mALRenderUtil = std::make_unique<ALRenderUtil>();
gOctreeMaxCapacity = gSavedSettings.getU32("OctreeMaxNodeCapacity");
gOctreeMinSize = gSavedSettings.getF32("OctreeMinimumNodeSize");
sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD");
......@@ -551,6 +553,9 @@ void LLPipeline::init()
mDeferredVB = new LLVertexBuffer(DEFERRED_VB_MASK, 0);
mDeferredVB->allocateBuffer(8, 0, true);
mALRenderUtil->restoreVertexBuffers();
setLightingDetail(-1);
//
......@@ -727,6 +732,8 @@ void LLPipeline::cleanup()
mDeferredVB = NULL;
mCubeVB = NULL;
mALRenderUtil.reset();
}
//============================================================================
......@@ -7527,6 +7534,8 @@ void LLPipeline::doResetVertexBuffers(bool forced)
mCubeVB = NULL;
mALRenderUtil->resetVertexBuffers();
for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList())
{
for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++)
......@@ -7594,7 +7603,7 @@ void LLPipeline::doResetVertexBuffers(bool forced)
mDeferredVB = new LLVertexBuffer(DEFERRED_VB_MASK, 0);
mDeferredVB->allocateBuffer(8, 0, true);
mALRenderUtil->restoreVertexBuffers();
}
void LLPipeline::renderObjects(U32 type, U32 mask, bool texture, bool batch_texture)
......@@ -9367,54 +9376,10 @@ void LLPipeline::renderDeferredLighting(LLRenderTarget *screen_target)
screen_target->flush();
// gamma correct lighting
gGL.matrixMode(LLRender::MM_PROJECTION);
gGL.pushMatrix();
gGL.loadIdentity();
gGL.matrixMode(LLRender::MM_MODELVIEW);