Commit 0220a7fe authored by Rye Mutt's avatar Rye Mutt 🍞
Browse files

Basic color grading support

parent 04e3bd07
......@@ -1413,6 +1413,10 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("sun_up_factor");
mReservedUniforms.push_back("moonlight_color");
// Alchemy
mReservedUniforms.push_back("colorgrade_lut");
mReservedUniforms.push_back("colorgrade_lut_size");
llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS);
std::set<std::string> dupe_check;
......
......@@ -257,6 +257,8 @@ class LLShaderMgr
WATER_EDGE_FACTOR, // "water_edge"
SUN_UP_FACTOR, // "sun_up_factor"
MOONLIGHT_COLOR, // "moonlight_color"
COLORGRADE_LUT, // "colorgrade_lut"
COLORGRADE_LUT_SIZE, // "colorgrade_lut_size"
END_RESERVED_UNIFORMS
} eGLSLReservedUniforms;
// clang-format on
......
......@@ -28,6 +28,9 @@
#include "alrenderutils.h"
#include "llimagepng.h"
#include "llimagetga.h"
#include "llimagewebp.h"
#include "llrendertarget.h"
#include "llvertexbuffer.h"
......@@ -50,6 +53,7 @@ static LLStaticHashedString tone_uncharted_c("tone_uncharted_c");
ALRenderUtil::ALRenderUtil()
{
// Connect settings
gSavedSettings.getControl("RenderColorGradeLUT")->getSignal()->connect(boost::bind(&ALRenderUtil::setupColorGrade, this));
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));
......@@ -85,9 +89,19 @@ void ALRenderUtil::resetVertexBuffers()
mRenderBuffer = nullptr;
}
void ALRenderUtil::releaseGLBuffers()
{
if (mCGLut)
{
LLImageGL::deleteTextures(1, &mCGLut);
mCGLut = 0;
}
}
void ALRenderUtil::refreshState()
{
setupTonemap();
setupColorGrade();
}
bool ALRenderUtil::setupTonemap()
......@@ -131,7 +145,7 @@ void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
dst->bindTarget();
LLGLSLShader* tone_shader = &gDeferredPostTonemapProgram[mTonemapType];
LLGLSLShader* tone_shader = (mCGLut != 0 ) ? &gDeferredPostColorGradeLUTProgram[mTonemapType] : &gDeferredPostTonemapProgram[mTonemapType];
tone_shader->bind();
......@@ -168,6 +182,19 @@ void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
}
}
if (mCGLut != 0)
{
S32 channel = tone_shader->enableTexture(LLShaderMgr::COLORGRADE_LUT, LLTexUnit::TT_TEXTURE);
if (channel > -1)
{
gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mCGLut);
gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
}
tone_shader->uniform4fv(LLShaderMgr::COLORGRADE_LUT_SIZE, 1, mCGLutSize.mV);
}
mRenderBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX);
mRenderBuffer->drawArrays(LLRender::TRIANGLES, 0, 3);
stop_glerror();
......@@ -179,4 +206,121 @@ void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
gGL.popMatrix();
gGL.matrixMode(LLRender::MM_MODELVIEW);
gGL.popMatrix();
}
bool ALRenderUtil::setupColorGrade()
{
if (mCGLut)
{
LLImageGL::deleteTextures(1, &mCGLut);
mCGLut = 0;
}
std::string lut_name = gSavedSettings.getString("RenderColorGradeLUT");
if (!lut_name.empty())
{
enum class ELutExt
{
EXT_IMG_TGA = 0,
EXT_IMG_PNG,
EXT_IMG_WEBP,
EXT_NONE
};
std::string lut_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "colorlut", lut_name);
if (!lut_path.empty())
{
std::string temp_exten = gDirUtilp->getExtension(lut_path);
ELutExt extension = ELutExt::EXT_NONE;
if (temp_exten == "tga")
{
extension = ELutExt::EXT_IMG_TGA;
}
else if (temp_exten == "png")
{
extension = ELutExt::EXT_IMG_PNG;
}
else if (temp_exten == "webp")
{
extension = ELutExt::EXT_IMG_WEBP;
}
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
bool decode_success = false;
switch (extension)
{
default:
break;
case ELutExt::EXT_IMG_TGA:
{
LLPointer<LLImageTGA> tga_image = new LLImageTGA;
if (tga_image->load(lut_path) && tga_image->decode(raw_image, 0.0f))
{
decode_success = true;
}
break;
}
case ELutExt::EXT_IMG_PNG:
{
LLPointer<LLImagePNG> png_image = new LLImagePNG;
if (png_image->load(lut_path) && png_image->decode(raw_image, 0.0f))
{
decode_success = true;
}
break;
}
case ELutExt::EXT_IMG_WEBP:
{
LLPointer<LLImageWebP> webp_image = new LLImageWebP;
if (webp_image->load(lut_path) && webp_image->decode(raw_image, 0.0f))
{
decode_success = true;
}
break;
}
}
if(decode_success)
{
U32 primary_format = 0;
U32 int_format = 0;
switch (raw_image->getComponents())
{
case 3:
{
primary_format = GL_RGB;
int_format = GL_RGB8;
break;
}
case 4:
{
primary_format = GL_RGBA;
int_format = GL_RGBA8;
break;
}
default:
return true;
};
S32 image_height = raw_image->getHeight();
S32 image_width = raw_image->getWidth();
if ((image_height > 0 && image_height <= 64) // within dimension limit
&& !(image_height & (image_height - 1)) // height is power of 2
&& ((image_height * image_height) == image_width)) // width is height * height
{
mCGLutSize = LLVector4(1.f / image_width, 1.f / image_height, (F32)image_width, (F32)image_height);
LLImageGL::generateTextures(1, &mCGLut);
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mCGLut);
LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, int_format, image_width,
image_height, primary_format, GL_UNSIGNED_BYTE, raw_image->getData(), false);
stop_glerror();
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
}
}
}
}
return true;
}
\ No newline at end of file
......@@ -42,6 +42,8 @@ class ALRenderUtil
void restoreVertexBuffers();
void resetVertexBuffers();
void releaseGLBuffers();
void refreshState();
// Deferred Only Functions
......@@ -63,6 +65,8 @@ class ALRenderUtil
void renderTonemap(LLRenderTarget* src, LLRenderTarget* dst);
// End Deferred Only
bool setupColorGrade();
private:
// Parameters
F32 mTonemapExposure = 1.f;
......@@ -77,6 +81,10 @@ class ALRenderUtil
LLVector3 mToneUnchartedParamB;
LLVector3 mToneUnchartedParamC;
// Texture Data
U32 mCGLut;
LLVector4 mCGLutSize;
// Vertex Buffers
LLPointer<LLVertexBuffer> mRenderBuffer;
};
......@@ -695,6 +695,17 @@
<key>Value</key>
<real>0.275</real>
</map>
<key>RenderColorGradeLUT</key>
<map>
<key>Comment</key>
<string>Name of image file for color grading LUT(TGA, PNG, or WebP)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>RenderToneMapExposure</key>
<map>
<key>Comment</key>
......
......@@ -37,6 +37,11 @@ uniform sampler2DRect diffuseRect;
uniform vec2 screen_res;
uniform float exposure;
#if COLOR_GRADE_LUT
uniform sampler2D colorgrade_lut;
uniform vec4 colorgrade_lut_size;
#endif
vec3 linear_to_srgb(vec3 cl);
vec3 reinhard(vec3 x)
......@@ -204,6 +209,30 @@ void main()
#if NEEDS_GAMMA_CORRECT
diff.rgb = linear_to_srgb(diff.rgb);
#endif
#if COLOR_GRADE_LUT
// Invert coord for compat with DX-style LUT
diff.y = 1.0 - diff.y;
// Convert to texel coords
vec3 lutRange = diff.rgb * ( colorgrade_lut_size.w - 1);
// Calculate coords in texel space
vec2 lutX = vec2(floor(lutRange.z)*colorgrade_lut_size.w+lutRange.x, lutRange.y);
vec2 lutY = vec2(ceil(lutRange.z)*colorgrade_lut_size.w+lutRange.x, lutRange.y);
// texel to ndc
lutX = (lutX+0.5)*colorgrade_lut_size.xy;
lutY = (lutY+0.5)*colorgrade_lut_size.xy;
// LUT interpolation
diff.rgb = mix(
texture2D(colorgrade_lut, lutX).rgb,
texture2D(colorgrade_lut, lutY).rgb,
fract(lutRange.z)
);
#endif
frag_color = diff;
}
......@@ -246,6 +246,7 @@ LLGLSLShader gDeferredSkinnedFullbrightProgram;
LLGLSLShader gNormalMapGenProgram;
LLGLSLShader gDeferredPostCASProgram;
LLGLSLShader gDeferredPostTonemapProgram[AL_TONEMAP_COUNT];
LLGLSLShader gDeferredPostColorGradeLUTProgram[AL_TONEMAP_COUNT];
// [RLVa:KB] - @setsphere
LLGLSLShader gRlvSphereProgram;
// [/RLVa:KB]
......@@ -835,6 +836,7 @@ void LLViewerShaderMgr::unloadShaders()
for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
{
gDeferredPostTonemapProgram[i].unload();
gDeferredPostColorGradeLUTProgram[i].unload();
}
mShaderLevel[SHADER_LIGHTING] = 0;
......@@ -1300,6 +1302,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
{
gDeferredPostTonemapProgram[i].unload();
gDeferredPostColorGradeLUTProgram[i].unload();
}
return TRUE;
}
......@@ -2997,6 +3000,22 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
success = gDeferredPostTonemapProgram[i].createShader(NULL, NULL);
}
if (success)
{
gDeferredPostColorGradeLUTProgram[i].mName = "Color Grading Shader " + std::to_string(i);
gDeferredPostColorGradeLUTProgram[i].mFeatures.hasSrgb = true;
gDeferredPostColorGradeLUTProgram[i].mShaderFiles.clear();
gDeferredPostColorGradeLUTProgram[i].mShaderFiles.push_back(make_pair("alchemy/postNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredPostColorGradeLUTProgram[i].mShaderFiles.push_back(make_pair("alchemy/toneMapF.glsl", GL_FRAGMENT_SHADER));
gDeferredPostColorGradeLUTProgram[i].mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredPostColorGradeLUTProgram[i].clearPermutations();
gDeferredPostColorGradeLUTProgram[i].addPermutation("COLOR_GRADE_LUT", std::to_string(1));
gDeferredPostColorGradeLUTProgram[i].addPermutation("TONEMAP_METHOD", std::to_string(i));
success = gDeferredPostColorGradeLUTProgram[i].createShader(NULL, NULL);
}
}
return success;
......
......@@ -332,6 +332,7 @@ extern LLGLSLShader gDeferredSkinnedFullbrightProgram;
extern LLGLSLShader gNormalMapGenProgram;
extern LLGLSLShader gDeferredPostCASProgram;
extern LLGLSLShader gDeferredPostTonemapProgram[AL_TONEMAP_COUNT];
extern LLGLSLShader gDeferredPostColorGradeLUTProgram[AL_TONEMAP_COUNT];
// [RLVa:KB] - @setsphere
extern LLGLSLShader gRlvSphereProgram;
// [/RLVa:KB]
......
......@@ -1294,6 +1294,8 @@ void LLPipeline::releaseGLBuffers()
mSampleMap = 0;
}
mALRenderUtil->releaseGLBuffers();
releaseLUTBuffers();
mWaterRef.release();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment