diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index e765e45e80d2aad8c84b4ad4db66e579f6424eb3..9e4a0fa5035ea316e7b28a1e9791599e314b853e 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -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; diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 97515df40a00804f866ee8d96064e05f483d32b3..df7c8bdceb6c13077d6536f4cb60d1176fde9d45 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -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 diff --git a/indra/newview/alrenderutils.cpp b/indra/newview/alrenderutils.cpp index cfe81f898be244f009eac4a0fd049e3b2be5c366..0238e601e839c41422d9c18422b1575e0c886f61 100644 --- a/indra/newview/alrenderutils.cpp +++ b/indra/newview/alrenderutils.cpp @@ -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 diff --git a/indra/newview/alrenderutils.h b/indra/newview/alrenderutils.h index 94c64a7d402ab6b52d5e7a9f9ee0286d0829ba60..cf4681b2cc07cfeb837fb796b04a1940fdb7251c 100644 --- a/indra/newview/alrenderutils.h +++ b/indra/newview/alrenderutils.h @@ -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; }; diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml index eb543c3dec2b9b8259725f2b41d7e2c601c84a9e..aad8ee9f6c7099ed1460bc1f1ec9708660f25cd1 100644 --- a/indra/newview/app_settings/settings_alchemy.xml +++ b/indra/newview/app_settings/settings_alchemy.xml @@ -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> diff --git a/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl b/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl index c53cb298663e9ca8656e850fe69f587168e8281f..2955f24cd07bf8c0a989a01e259c6347f845d467 100644 --- a/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl +++ b/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl @@ -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; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 34be5447ba50047748139cba5512b8dbdd199271..53c9b20cbb93de332611a23bed52e0ce69650b70 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -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; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index 0afa02480fa63439adf76f4d4069b58fc31b60d4..b34aa3646dab73981da520191ab243a406372277 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -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] diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index dfe947845c64e9420fc9779663188d7f90ebd0a8..060b66a1d478c14c0f9dd4701551b6f1e240a4a4 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1294,6 +1294,8 @@ void LLPipeline::releaseGLBuffers() mSampleMap = 0; } + mALRenderUtil->releaseGLBuffers(); + releaseLUTBuffers(); mWaterRef.release();