From 8fac98f28dc3b11723e8a9f487338768a61b1896 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Tue, 2 Jun 2026 22:20:30 -0700 Subject: [PATCH 1/3] HDR --- shaders/lib/lighting/mainLighting.glsl | 8 +- shaders/lib/misc/lensFlare.glsl | 6 +- shaders/lib/pipelineSettings.glsl | 8 ++ shaders/lib/uniforms.glsl | 13 +++ shaders/lib/util/commonFunctions.glsl | 20 ++++ shaders/program/composite.glsl | 3 + shaders/program/composite1.glsl | 5 +- shaders/program/composite5.glsl | 124 +++++++++++++++++++++++-- shaders/program/final.glsl | 19 +++- 9 files changed, 194 insertions(+), 12 deletions(-) diff --git a/shaders/lib/lighting/mainLighting.glsl b/shaders/lib/lighting/mainLighting.glsl index 0c788aa8..9d4f3547 100644 --- a/shaders/lib/lighting/mainLighting.glsl +++ b/shaders/lib/lighting/mainLighting.glsl @@ -609,7 +609,13 @@ void DoLighting(inout vec4 color, inout vec3 shadowMult, vec3 playerPos, vec3 vi #endif // Mix Colors - vec3 finalDiffuse = pow2(directionShade * vanillaAO) * (blockLighting + pow2(sceneLighting) + minLighting) + pow2(emission); + #if !defined HDR_ENABLED || !defined(IPBR) + vec3 finalDiffuse = pow2(directionShade * vanillaAO) * (blockLighting + pow2(sceneLighting) + minLighting) + pow2(emission); + #else + // For IPBR in HDR, remove pow2 deep fried feeling, and makeup by boost + //TODO: Better spot to put this to only effect IPBR results? + vec3 finalDiffuse = pow2(directionShade * vanillaAO) * (blockLighting + pow2(sceneLighting) + minLighting) + (emission * 4); + #endif finalDiffuse = sqrt(max(finalDiffuse, vec3(0.0))); // sqrt() for a bit more realistic light mix, max() to prevent NaNs // Apply Lighting diff --git a/shaders/lib/misc/lensFlare.glsl b/shaders/lib/misc/lensFlare.glsl index eefddd54..73e8b079 100644 --- a/shaders/lib/misc/lensFlare.glsl +++ b/shaders/lib/misc/lensFlare.glsl @@ -117,5 +117,9 @@ void DoLensFlare(inout vec3 color, vec3 viewPos, float dither) { flare *= flareFactor; - color = mix(color, vec3(1.0), flare); + #ifndef HDR_ENABLED + color = mix(color, vec3(1.0), flare); + #else + color += flare; // flare stands out better in HDR by simple addition + #endif } \ No newline at end of file diff --git a/shaders/lib/pipelineSettings.glsl b/shaders/lib/pipelineSettings.glsl index b9161a28..9dcc3099 100644 --- a/shaders/lib/pipelineSettings.glsl +++ b/shaders/lib/pipelineSettings.glsl @@ -15,6 +15,14 @@ const int colortex18Format = R8; //shadow for voxy chunks (needs repr const int colortex19Format = RGBA8; //scene image for voxy reflections (needs reprojection) */ +#ifdef HDR_ENABLED +// Iris will override declarations above with these +/* + const int colortex0Format = RGB16F; //HDR: upgraded, else visible sky banding + const int colortex3Format = RGBA16F; //HDR: upgraded to store HDR values to final +*/ +#endif + const bool colortex0Clear = true; const bool colortex1Clear = false; const bool colortex2Clear = false; diff --git a/shaders/lib/uniforms.glsl b/shaders/lib/uniforms.glsl index af304d3a..13ee05cf 100644 --- a/shaders/lib/uniforms.glsl +++ b/shaders/lib/uniforms.glsl @@ -151,6 +151,19 @@ uniform vec3 previousCameraPositionFract; uniform sampler2D vxDepthTexOpaque; #endif +#if defined HDR_ENABLED +// #undef HDR_ENABLED + uniform float HdrGamePeakBrightness; + uniform float HdrGamePaperWhiteBrightness; + uniform float HdrGameMinimumBrightness; + uniform float HdrUIBrightness; +#endif + +// #define HDR_ENABLED // HDRMod forced enable for testing +// const float HdrGamePeakBrightness = 1000.f; +// const float HdrGamePaperWhiteBrightness = 203.f; +// const float HdrUIBrightness = 203.f; + #if COLORED_LIGHTING_INTERNAL > 0 uniform usampler3D voxel_sampler; uniform sampler3D floodfill_sampler; diff --git a/shaders/lib/util/commonFunctions.glsl b/shaders/lib/util/commonFunctions.glsl index 47e39413..7b7cb8b6 100644 --- a/shaders/lib/util/commonFunctions.glsl +++ b/shaders/lib/util/commonFunctions.glsl @@ -313,4 +313,24 @@ vec3 hsv2rgb(vec3 c) vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void LinearToRGB(inout vec3 color) { // sRGB gamma encode vec3 + const vec3 k = vec3(0.055); + color = mix((vec3(1.0) + k) * pow(color, vec3(1.0 / 2.4)) - k, 12.92 * color, lessThan(color, vec3(0.0031308))); +} + +void LinearToRGB(inout float color) { // sRGB gamma encode float + const float k = (0.055); + color = (color < (0.0031308)) ? (12.92 * color) : ((1.0 + k) * pow(color, (1.0 / 2.4)) - k); +} + +void RGBToLinear(inout vec3 color) { // sRGB gamma decode vec3 + const vec3 k = vec3(0.055); + color = mix(pow((color + k) / (vec3(1.0) + k), vec3(2.4)), color / 12.92, lessThan(color, vec3(0.04045))); +} + +void RGBToLinear(inout float color) { // sRGB gamma decode float + const float k = (0.055); + color = (color < (0.04045)) ? (color / 12.92) : pow((color + k) / (1.0 + k), 2.4); } \ No newline at end of file diff --git a/shaders/program/composite.glsl b/shaders/program/composite.glsl index c5694645..3821c623 100644 --- a/shaders/program/composite.glsl +++ b/shaders/program/composite.glsl @@ -201,6 +201,9 @@ void main() { /* DRAWBUFFERS:7 */ gl_FragData[0] = reflectOutput; + #ifdef HDR_ENABLED + gl_FragData[0] = max(gl_FragData[0], vec4(0.0)); //Prob unneeded, but just in case + #endif // same check as #ifdef PBR_REFLECTIONS but for Optifine to understand: #if BLOCK_REFLECT_QUALITY >= 2 && RP_MODE >= 1 diff --git a/shaders/program/composite1.glsl b/shaders/program/composite1.glsl index 0fb35f02..4e39b571 100644 --- a/shaders/program/composite1.glsl +++ b/shaders/program/composite1.glsl @@ -326,7 +326,10 @@ void main() { /* DRAWBUFFERS:0 */ gl_FragData[0] = vec4(color, 1.0); - + #ifdef HDR_ENABLED + gl_FragData[0] = max(gl_FragData[0], vec4(0.0)); + #endif + // supposed to be #if defined LIGHTSHAFTS_ACTIVE && (LIGHTSHAFT_BEHAVIOUR == 1 && SHADOW_QUALITY >= 1 || defined END) #if LIGHTSHAFT_QUALI_DEFINE > 0 && LIGHTSHAFT_BEHAVIOUR == 1 && SHADOW_QUALITY >= 1 && defined OVERWORLD || defined END #if LENSFLARE_MODE > 0 || defined ENTITY_TAA_NOISY_CLOUD_FIX diff --git a/shaders/program/composite5.glsl b/shaders/program/composite5.glsl index d9105f23..095b7ddb 100644 --- a/shaders/program/composite5.glsl +++ b/shaders/program/composite5.glsl @@ -28,11 +28,6 @@ vec2 view = vec2(viewWidth, viewHeight); #endif //Common Functions// -void LinearToRGB(inout vec3 color) { - const vec3 k = vec3(0.055); - color = mix((vec3(1.0) + k) * pow(color, vec3(1.0 / 2.4)) - k, 12.92 * color, lessThan(color, vec3(0.0031308))); -} - void DoCompTonemap(inout vec3 color) { // Lottes tonemap modified for Complementary Shaders // Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines" @@ -86,13 +81,120 @@ void DoCompTonemap(inout vec3 color) { color = clamp01(colorOut); } +#ifdef HDR_ENABLED +// Reinhard Extended Piecewise https://github.com/clshortfuse/renodx/blob/main/src/shaders/tonemap/reinhard.hlsl +vec3 DoCompTonemapHDR_Reinhard(vec3 x, float peak) { return x / (x / peak + 1.0); } +vec3 DoCompTonemapHDR_ReinhardExt(vec3 x, float white_max, float peak) { + return DoCompTonemapHDR_Reinhard(x, peak) * (1.0 + (peak * x) / (white_max * white_max)); +} +float DoCompTonemapHDR_ReinhardScale(float w, float p, float m, float x, float y) { + return p * (w * w * y - (p * x * x)) / (w * w * x * (p - y)); +} +vec3 DoCompTonemapHDR_ReinhardPiece(vec3 x, float white_max, float x_max, float shoulder) { + const float x_min = 0.0f; + float exposure = DoCompTonemapHDR_ReinhardScale(white_max, x_max, x_min, shoulder, shoulder); + vec3 extended = DoCompTonemapHDR_ReinhardExt(x * exposure, white_max * exposure, x_max); + extended = min(extended, x_max); + return mix(x, extended, step(shoulder, x)); +} +void DoCompTonemapHDR(inout vec3 color) { + // HDR Lottes by stealing toe and midgray change from SDR & replacing shoulder with Reinhard Extended. + color = TM_EXPOSURE * color; + + // Setup + vec3 colorOut = color; + + // TM_CONTRAsT + colorOut /= 0.25; //midgray + colorOut = pow(colorOut, vec3(sqrt(TM_CONTRAST / 1.05))); + colorOut *= 0.25; + + // Setup for the rest + float initialLuminance = GetLuminance(colorOut); + float peak = HdrGamePeakBrightness / HdrGamePaperWhiteBrightness; + + // Tonemap to Peak + { + // Extension: Lottes piecewise steal toe and midgray change, but ignore shoulder + vec3 lower = colorOut; + //TODO: Find derivative and solve for TM_CONTRAST + //TODO: Move to a common function? + { + vec3 a = vec3(1.05); // General Contrast + vec3 d = vec3(1.0); // Roll-off control + vec3 hdrMax = vec3(8.0); // Maximum input brightness + vec3 midIn = vec3(0.25); // Input middle gray + vec3 midOut = vec3(0.25); // Output middle gray + + vec3 a_d = a * d; + vec3 hdrMaxA = pow(hdrMax, a); + vec3 hdrMaxAD = pow(hdrMax, a_d); + vec3 midInA = pow(midIn, a); + vec3 midInAD = pow(midIn, a_d); + vec3 HM1 = hdrMaxA * midOut; + vec3 HM2 = hdrMaxAD - midInAD; + + vec3 b = (-midInA + HM1) / (HM2 * midOut); + vec3 c = (hdrMaxAD * midInA - HM1 * midInAD) / (HM2 * midOut); + + lower = pow(color, a) / (pow(color, a_d) * b + c); + } + vec3 higher = colorOut + 0.01058414; + bvec3 thres = greaterThan(colorOut, vec3(0.11641)); + colorOut = mix(lower, higher, thres); + + // Shoulder + colorOut = DoCompTonemapHDR_ReinhardPiece(colorOut, 100, peak, 0.11641); + } + + // sRGB Gamma Encode + LinearToRGB(colorOut); + + // Path to White (From SDR, but reduced and to new HDR peak) + float white = peak; + LinearToRGB(white); + const float wpInputCurveStart = 0.0; + const float wpInputCurveMax = 200.0; // Increase this value to reduce the effect of white path + float modifiedLuminance = pow(initialLuminance / wpInputCurveMax, 2.0 - TM_WHITE_PATH) * wpInputCurveMax; + float whitePath = smoothstep(wpInputCurveStart, wpInputCurveMax, modifiedLuminance); + colorOut = mix(colorOut, vec3(white), whitePath); + + // Remove tonemapping from darker colors for better readability (From SDR) + const float darkLiftStart = 0.1; + const float darkLiftMix = 0.75; + float darkLift = smoothstep(darkLiftStart, 0.0, initialLuminance); + vec3 smoothColor = pow(color, vec3(1.0 / 2.2)); + colorOut = mix(colorOut, smoothColor, darkLift * darkLiftMix * max0(0.55 - abs(1.05 - TM_CONTRAST)) / 0.55); + + // Desaturate dark colors (From SDR) + const float dpInputCurveStart = 0.1; + const float dpInputCurveMax = 0.0; + float desaturatePath = smoothstep(dpInputCurveStart, dpInputCurveMax, initialLuminance); + colorOut = mix(colorOut, vec3(GetLuminance(colorOut)), desaturatePath * TM_DARK_DESATURATION); + + // Clamp against negatives and NaNs + color = max(vec3(0.0), colorOut); +} +#endif + void DoBSLColorSaturation(inout vec3 color) { + #ifdef HDR_ENABLED + // Cram + float p = HdrGamePeakBrightness / HdrGamePaperWhiteBrightness; + color /= p; + #endif + float saturationFactor = T_SATURATION + 0.07; float grayVibrance = (color.r + color.g + color.b) / 3.0; float graySaturation = grayVibrance; if (saturationFactor < 1.00) graySaturation = dot(color, vec3(0.299, 0.587, 0.114)); + #ifdef HDR_ENABLED + grayVibrance /= p; + graySaturation /= p; + #endif + float mn = min(color.r, min(color.g, color.b)); float mx = max(color.r, max(color.g, color.b)); float sat = (1.0 - (mx - mn)) * (1.0 - mx) * grayVibrance * 5.0; @@ -101,6 +203,10 @@ void DoBSLColorSaturation(inout vec3 color) { color = mix(color, mix(color, lightness, 1.0 - T_VIBRANCE), sat); color = mix(color, lightness, (1.0 - lightness) * (2.0 - T_VIBRANCE) / 2.0 * abs(T_VIBRANCE - 1.0)); color = color * saturationFactor - graySaturation * (saturationFactor - 1.0); + + #ifdef HDR_ENABLED + color *= p; + #endif } #if BLOOM_ENABLED == 1 @@ -206,7 +312,13 @@ void main() { color *= 0.01; #endif - DoCompTonemap(color); + ////////// SDR/HDR output splits here! //////////// + + #ifndef HDR_ENABLED + DoCompTonemap(color); + #else + DoCompTonemapHDR(color); + #endif #if defined GREEN_SCREEN_LIME || SELECT_OUTLINE == 4 int materialMaskInt = int(texelFetch(colortex6, texelCoord, 0).g * 255.1); diff --git a/shaders/program/final.glsl b/shaders/program/final.glsl index dda1c202..a3bd26a8 100644 --- a/shaders/program/final.glsl +++ b/shaders/program/final.glsl @@ -152,12 +152,25 @@ void main() { #ifdef VIGNETTE_R vec2 texCoordMin = texCoordM.xy - 0.5; - float vignette = 1.0 - dot(texCoordMin, texCoordMin) * (1.0 - GetLuminance(color)); + #ifndef HDR_ENABLED + float vignette = 1.0 - dot(texCoordMin, texCoordMin) * (1.0 - GetLuminance(color)); + #else + float vignette = 1.0 - dot(texCoordMin, texCoordMin) * (1.0 - clamp(GetLuminance(color), 0, 1)); + #endif color *= vignette; #endif - float dither = texture2DLod(noisetex, texCoord * view / 128.0, 0.0).b; - color += vec3((dither - 0.25) / 128.0); + #ifndef HDR_ENABLED + float dither = texture2DLod(noisetex, texCoord * view / 128.0, 0.0).b; + color += vec3((dither - 0.25) / 128.0); + #endif + + #ifdef HDR_ENABLED + RGBToLinear(color); // Decode + color = min(color, vec3(HdrGamePeakBrightness / HdrGamePaperWhiteBrightness)); // Clamp Peak + color *= HdrGamePaperWhiteBrightness / HdrUIBrightness; // UI Scaling + LinearToRGB(color); // Encode s + #endif /* DRAWBUFFERS:0 */ gl_FragData[0] = vec4(color, 1.0); From 85ec0c0141c09e384425ae171f16d979cd9a6c48 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Tue, 2 Jun 2026 22:41:30 -0700 Subject: [PATCH 2/3] typos --- shaders/lib/lighting/mainLighting.glsl | 2 +- shaders/program/final.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shaders/lib/lighting/mainLighting.glsl b/shaders/lib/lighting/mainLighting.glsl index 9d4f3547..ab1c7c06 100644 --- a/shaders/lib/lighting/mainLighting.glsl +++ b/shaders/lib/lighting/mainLighting.glsl @@ -613,7 +613,7 @@ void DoLighting(inout vec4 color, inout vec3 shadowMult, vec3 playerPos, vec3 vi vec3 finalDiffuse = pow2(directionShade * vanillaAO) * (blockLighting + pow2(sceneLighting) + minLighting) + pow2(emission); #else // For IPBR in HDR, remove pow2 deep fried feeling, and makeup by boost - //TODO: Better spot to put this to only effect IPBR results? + //TODO: Better spot to put this to only affect IPBR results without this ugly branching? vec3 finalDiffuse = pow2(directionShade * vanillaAO) * (blockLighting + pow2(sceneLighting) + minLighting) + (emission * 4); #endif finalDiffuse = sqrt(max(finalDiffuse, vec3(0.0))); // sqrt() for a bit more realistic light mix, max() to prevent NaNs diff --git a/shaders/program/final.glsl b/shaders/program/final.glsl index a3bd26a8..efddd020 100644 --- a/shaders/program/final.glsl +++ b/shaders/program/final.glsl @@ -169,7 +169,7 @@ void main() { RGBToLinear(color); // Decode color = min(color, vec3(HdrGamePeakBrightness / HdrGamePaperWhiteBrightness)); // Clamp Peak color *= HdrGamePaperWhiteBrightness / HdrUIBrightness; // UI Scaling - LinearToRGB(color); // Encode s + LinearToRGB(color); // Encode #endif /* DRAWBUFFERS:0 */ From c0b987a9bfa22532033d9272a7e4eccc8799109b Mon Sep 17 00:00:00 2001 From: David Huynh Date: Tue, 2 Jun 2026 22:47:42 -0700 Subject: [PATCH 3/3] desmos link --- shaders/program/composite5.glsl | 1 + 1 file changed, 1 insertion(+) diff --git a/shaders/program/composite5.glsl b/shaders/program/composite5.glsl index 095b7ddb..ce7a6b52 100644 --- a/shaders/program/composite5.glsl +++ b/shaders/program/composite5.glsl @@ -116,6 +116,7 @@ void DoCompTonemapHDR(inout vec3 color) { // Tonemap to Peak { // Extension: Lottes piecewise steal toe and midgray change, but ignore shoulder + // https://www.desmos.com/calculator/bdi1hggcqy vec3 lower = colorOut; //TODO: Find derivative and solve for TM_CONTRAST //TODO: Move to a common function?