renderer: shader variants refactor (#13434)

Part 0 of renderer reworks.
This commit is contained in:
UjinT34 2026-03-07 00:05:10 +03:00 committed by GitHub
parent 8685fd7b0c
commit a5858018d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 1952 additions and 1472 deletions

View file

@ -125,6 +125,7 @@ find_package(Threads REQUIRED)
set(GLES_VERSION "GLES3")
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
find_package(glslang CONFIG REQUIRED)
set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
set(HYPRLANG_MINIMUM_VERSION 0.6.7)
@ -479,9 +480,9 @@ function(protocolWayland)
endfunction()
if(TARGET OpenGL::GL)
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL Threads::Threads)
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
else()
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 Threads::Threads)
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
endif()
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)

View file

@ -12,6 +12,7 @@
epoll-shim,
git,
glaze-hyprland,
glslang,
gtest,
hyprcursor,
hyprgraphics,
@ -173,6 +174,7 @@ customStdenv.mkDerivation (finalAttrs: {
cairo
git
glaze-hyprland
glslang
gtest
hyprcursor
hyprgraphics

View file

@ -15,7 +15,7 @@ echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/rend
for filename in `ls ${SHADERS_SRC}`; do
echo "-- ${filename}"
{ echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
{ echo -n 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
echo "}," >> ./src/render/shaders/Shaders.hpp

View file

@ -1585,6 +1585,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
{
.value = "render:use_shader_blur_blend",
.description = "Use experimental blurred bg blending",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* cursor:

View file

@ -800,6 +800,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("render:cm_sdr_eotf", {"default"});
registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1});
registerConfigVar("render:icc_vcgt_enabled", Hyprlang::INT{1});
registerConfigVar("render:use_shader_blur_blend", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});

View file

@ -2049,7 +2049,12 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques
}
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
if (g_pHyprOpenGL->initShaders())
CVarList vars(request, 0, ' ');
if (vars.size() > 2)
return "too many args";
if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : ""))
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
else
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
@ -2076,8 +2081,8 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders});
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});

View file

@ -297,22 +297,6 @@ int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) {
return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt));
}
uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) {
switch (drm) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010: return GL_RGB10_A2;
default: return GL_RGBA;
}
UNREACHABLE();
return GL_RGBA;
}
uint32_t NFormatUtils::glFormatToType(uint32_t gl) {
return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE;
}
std::string NFormatUtils::drmFormatName(DRMFormat drm) {
auto n = drmGetFormatName(drm);

View file

@ -53,8 +53,6 @@ namespace NFormatUtils {
bool isFormatOpaque(DRMFormat drm);
int pixelsPerBlock(const SPixelFormat* const fmt);
int minStride(const SPixelFormat* const fmt, int32_t width);
uint32_t drmFormatToGL(DRMFormat drm);
uint32_t glFormatToType(uint32_t gl);
std::string drmFormatName(DRMFormat drm);
std::string drmModifierName(uint64_t mod);
DRMFormat alphaFormat(DRMFormat prevFormat);

View file

@ -1579,10 +1579,20 @@ Vector2D CMonitor::middle() {
return m_position + m_size / 2.f;
}
const Mat3x3& CMonitor::getTransformMatrix() {
return m_projMatrix;
}
const Mat3x3& CMonitor::getScaleMatrix() {
return m_projOutputMatrix;
}
void CMonitor::updateMatrix() {
m_projMatrix = Mat3x3::identity();
if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL)
m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0);
m_projOutputMatrix = Mat3x3::outputProjection(m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL);
}
WORKSPACEID CMonitor::activeWorkspaceID() {

View file

@ -127,7 +127,7 @@ class CMonitor {
bool m_scheduledRecalc = false;
wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL;
float m_xwaylandScale = 1.f;
Mat3x3 m_projMatrix;
std::optional<Vector2D> m_forceSize;
SP<Aquamarine::SOutputMode> m_currentMode;
SP<Aquamarine::CSwapchain> m_cursorSwapchain;
@ -303,7 +303,6 @@ class CMonitor {
void setSpecialWorkspace(const WORKSPACEID& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
void updateMatrix();
WORKSPACEID activeWorkspaceID();
WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox();
@ -335,6 +334,10 @@ class CMonitor {
bool inHDR();
bool gammaRampsInUse();
//
const Mat3x3& getTransformMatrix();
const Mat3x3& getScaleMatrix();
/// Has an active workspace with a real fullscreen window (includes special workspace)
bool inFullscreenMode();
/// Get fullscreen window from active or special workspace
@ -364,7 +367,12 @@ class CMonitor {
return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name;
}
Mat3x3 m_projMatrix;
private:
void updateMatrix();
Mat3x3 m_projOutputMatrix;
void setupDefaultWS(const SMonitorRule&);
WORKSPACEID findAvailableDefaultWS();
void commitDPMSState(bool state);

View file

@ -26,7 +26,10 @@ std::string NTransferFunction::toString(eTF tf) {
return "";
}
eTF NTransferFunction::fromConfig() {
eTF NTransferFunction::fromConfig(bool useICC) {
if (useICC)
return TF_SRGB;
static auto PSDREOTF = CConfigValue<Hyprlang::STRING>("render:cm_sdr_eotf");
static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF);
static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); });

View file

@ -15,5 +15,5 @@ namespace NTransferFunction {
eTF fromString(const std::string tfName);
std::string toString(eTF tf);
eTF fromConfig();
eTF fromConfig(bool useICC = false);
}

View file

@ -322,18 +322,22 @@ namespace NColorManagement {
using PImageDescription = WP<const CImageDescription>;
static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB),
.luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}});
static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB),
.luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80},
});
static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203},
});
static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}});
;
static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR,
.windowsScRGB = true,
@ -342,6 +346,6 @@ namespace NColorManagement {
.primaries = NColorPrimaries::BT709,
.luminances = {.reference = 203},
});
;
}
static const auto LINEAR_IMAGE_DESCRIPTION = SCRGB_IMAGE_DESCRIPTION; // TODO any reason to use something different?
}

View file

@ -162,8 +162,9 @@ void CScreenshareFrame::renderMonitor() {
if (!g_pHyprOpenGL->m_monitorRenderResources.contains(PMONITOR))
return; // wtf?
auto TEXTURE = g_pHyprOpenGL->m_monitorRenderResources[PMONITOR].monitorMirrorFB.getTexture();
auto TEXTURE = g_pHyprOpenGL->m_monitorRenderResources[PMONITOR].monitorMirrorFB.getTexture();
const bool IS_CM_AWARE = PROTO::colorManagement && PROTO::colorManagement->isClientCMAware(m_session->m_client);
g_pHyprOpenGL->m_renderData.transformDamage = false;
g_pHyprOpenGL->m_renderData.noSimplify = true;
@ -173,7 +174,11 @@ void CScreenshareFrame::renderMonitor() {
.translate(-m_session->m_captureBox.pos()); // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
g_pHyprOpenGL->pushMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTexturePrimitive(TEXTURE, monbox);
g_pHyprOpenGL->renderTexture(TEXTURE, monbox,
{
.cmBackToSRGB = !IS_CM_AWARE,
.cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr,
});
g_pHyprOpenGL->setRenderModifEnabled(true);
g_pHyprOpenGL->popMonitorTransformEnabled();

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,7 @@
#include "../debug/TracyDefines.hpp"
#include "../protocols/core/Compositor.hpp"
#include "render/ShaderLoader.hpp"
struct gbm_device;
class CHyprRenderer;
@ -87,54 +88,23 @@ enum eMonitorExtraRenderFBs : uint8_t {
FB_MONITOR_RENDER_EXTRA_BLUR,
};
enum ePreparedFragmentShader : uint8_t {
SH_FRAG_QUAD = 0,
SH_FRAG_PASSTHRURGBA,
SH_FRAG_MATTE,
SH_FRAG_EXT,
SH_FRAG_BLUR1,
SH_FRAG_BLUR2,
SH_FRAG_CM_BLURPREPARE,
SH_FRAG_BLURPREPARE,
SH_FRAG_BLURFINISH,
SH_FRAG_SHADOW,
SH_FRAG_CM_BORDER1,
SH_FRAG_BORDER1,
SH_FRAG_GLITCH,
SH_FRAG_LAST,
};
enum ePreparedFragmentShaderFeature : uint8_t {
SH_FEAT_UNKNOWN = 0, // all features just in case
SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling
SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling
SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint
SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0
SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM
SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01
SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1)
// uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD
};
struct SFragShaderDesc {
ePreparedFragmentShader id;
const char* file;
Render::ePreparedFragmentShader id;
const char* file;
};
struct SPreparedShaders {
SPreparedShaders() {
for (auto& f : frag) {
f = makeShared<CShader>();
}
}
// SPreparedShaders() {
// for (auto& f : frag) {
// f = makeShared<CShader>();
// }
// }
std::string TEXVERTSRC;
std::string TEXVERTSRC320;
std::array<SP<CShader>, SH_FRAG_LAST> frag;
std::map<uint8_t, SP<CShader>> fragVariants;
std::string TEXVERTSRC;
std::string TEXVERTSRC320;
// std::array<SP<CShader>, SH_FRAG_LAST> frag;
// std::map<uint8_t, SP<CShader>> fragVariants;
std::array<std::map<Render::ShaderFeatureFlags, SP<CShader>>, Render::SH_FRAG_LAST> fragVariants;
};
struct SMonitorRenderData {
@ -242,6 +212,8 @@ class CHyprOpenGLImpl {
bool cmBackToSRGB = false;
bool noCM = false;
bool finalMonitorCM = false;
SP<CMonitor> cmBackToSRGBSource;
SP<CTexture> blurredBG;
};
struct SBorderRenderData {
@ -316,18 +288,15 @@ class CHyprOpenGLImpl {
std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format);
EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs);
bool initShaders();
bool initShaders(const std::string& path = "");
WP<CShader> useShader(WP<CShader> prog);
void ensureLockTexturesRendered(bool load);
bool explicitSyncSupported();
WP<CShader> getSurfaceShader(uint8_t features);
WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0);
bool m_shadersInitialized = false;
SP<SPreparedShaders> m_shaders;
std::map<std::string, std::string> m_includes;
SCurrentRenderData m_renderData;
@ -431,6 +400,7 @@ class CHyprOpenGLImpl {
void initEGL(bool gbm);
EGLDeviceEXT eglDeviceFromDRMFD(int drmFD);
void initAssets();
void ensureLockTexturesRendered(bool load);
void initMissingAssetTexture();
void requestBackgroundResource();
@ -454,6 +424,8 @@ class CHyprOpenGLImpl {
void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data);
void renderRectWithBlurInternal(const CBox&, const CHyprColor&, const SRectRenderData& data);
void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data);
WP<CShader> renderToOutputInternal();
WP<CShader> renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox);
void renderTextureInternal(SP<CTexture>, const CBox&, const STextureRenderData& data);
void renderTextureWithBlurInternal(SP<CTexture>, const CBox&, const STextureRenderData& data);

View file

@ -41,6 +41,7 @@ CRenderbuffer::CRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format) :
glGenFramebuffers(1, &m_framebuffer.m_fb);
m_framebuffer.m_fbAllocated = true;
m_framebuffer.m_size = buffer->size;
m_framebuffer.m_drmFormat = dma.format;
m_framebuffer.bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);

View file

@ -32,6 +32,7 @@
#include "../event/EventBus.hpp"
#include "helpers/CursorShapes.hpp"
#include "helpers/Monitor.hpp"
#include "helpers/cm/ColorManagement.hpp"
#include "pass/TexPassElement.hpp"
#include "pass/ClearPassElement.hpp"
#include "pass/RectPassElement.hpp"
@ -41,7 +42,6 @@
#include "../protocols/ColorManagement.hpp"
#include "../protocols/types/ContentType.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../event/EventBus.hpp"
#include "render/OpenGL.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
@ -1260,6 +1260,79 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
}
}
static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) {
// might be too strict
return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB ||
imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) &&
(targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ||
targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG);
}
static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) {
// might be too strict
return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ||
imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG) &&
(targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB ||
targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22);
}
SCMSettings CHyprRenderer::getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription,
SP<CWLSurfaceResource> surface, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) {
const auto sdrEOTF = NTransferFunction::fromConfig();
NColorManagement::eTransferFunction srcTF;
auto& m_renderData = g_pHyprOpenGL->m_renderData;
if (m_renderData.surface.valid()) {
if (m_renderData.surface->m_colorManagement.valid()) {
if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB)
srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22;
else
srcTF = imageDescription->value().transferFunction;
} else if (sdrEOTF == NTransferFunction::TF_SRGB)
srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB;
else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22)
srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22;
else
srcTF = imageDescription->value().transferFunction;
} else
srcTF = imageDescription->value().transferFunction;
const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value());
const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value());
const float maxLuminance = needsHDRmod ?
imageDescription->value().getTFMaxLuminance(-1) :
(imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference);
const auto dstMaxLuminance = targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000;
auto matrix = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries());
auto toXYZ = targetImageDescription->getPrimaries()->value().toXYZ();
const bool needsMod = (imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) &&
targetImageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ &&
((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) ||
(m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f));
return {
.sourceTF = srcTF,
.targetTF = targetImageDescription->value().transferFunction,
.srcTFRange = {.min = imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
.max = imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)},
.dstTFRange = {.min = targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
.max = targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)},
.srcRefLuminance = imageDescription->value().luminances.reference,
.dstRefLuminance = targetImageDescription->value().luminances.reference,
.convertMatrix = matrix.mat(),
.needsTonemap = maxLuminance >= dstMaxLuminance * 1.01,
.maxLuminance = maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference,
.dstMaxLuminance = dstMaxLuminance,
.dstPrimaries2XYZ = toXYZ.mat(),
.needsSDRmod = needsMod,
.sdrSaturation = needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f,
.sdrBrightnessMultiplier = needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f,
};
}
void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now();
@ -2200,10 +2273,8 @@ std::tuple<float, float, float> CHyprRenderer::getRenderTimes(PHLMONITOR pMonito
float maxRenderTime = 0;
float minRenderTime = 9999;
for (auto const& rt : POVERLAY->m_lastRenderTimes) {
if (rt > maxRenderTime)
maxRenderTime = rt;
if (rt < minRenderTime)
minRenderTime = rt;
maxRenderTime = std::max(rt, maxRenderTime);
minRenderTime = std::min(rt, minRenderTime);
avgRenderTime += rt;
}
avgRenderTime /= POVERLAY->m_lastRenderTimes.empty() ? 1 : POVERLAY->m_lastRenderTimes.size();
@ -2706,6 +2777,16 @@ void CHyprRenderer::renderSnapshot(WP<Desktop::View::CPopup> popup) {
m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
}
NColorManagement::PImageDescription CHyprRenderer::workBufferImageDescription() {
const auto& m_renderData = g_pHyprOpenGL->m_renderData;
// TODO
// const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present;
// const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC);
// const auto CHOSEN_SDR_EOTF = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB;
return m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF});
}
bool CHyprRenderer::shouldBlur(PHLLS ls) {
if (m_bRenderingSnapshot)
return false;
@ -2729,3 +2810,7 @@ bool CHyprRenderer::shouldBlur(WP<Desktop::View::CPopup> p) {
return *PBLURPOPUPS && *PBLUR;
}
bool CHyprRenderer::reloadShaders(const std::string& path) {
return g_pHyprOpenGL->initShaders(path);
}

View file

@ -1,7 +1,10 @@
#pragma once
#include "../defines.hpp"
#include <hyprgraphics/color/Color.hpp>
#include <hyprutils/math/Box.hpp>
#include <list>
#include <optional>
#include "../helpers/Monitor.hpp"
#include "../desktop/view/LayerSurface.hpp"
#include "OpenGL.hpp"
@ -10,12 +13,21 @@
#include "../helpers/math/Math.hpp"
#include "../helpers/time/Time.hpp"
#include "../../protocols/cursor-shape-v1.hpp"
#include "helpers/cm/ColorManagement.hpp"
struct SMonitorRule;
class CWorkspace;
class CInputPopup;
class IHLBuffer;
class CEventLoopTimer;
const std::vector<const char*> ASSET_PATHS = {
#ifdef DATAROOTDIR
DATAROOTDIR,
#endif
"/usr/share",
"/usr/local/share",
};
class CToplevelExportProtocolManager;
class CInputManager;
struct SSessionLockSurface;
@ -48,6 +60,29 @@ struct SRenderWorkspaceUntilData {
PHLWINDOW w;
};
struct STFRange {
float min = 0;
float max = 80;
};
struct SCMSettings {
NColorManagement::eTransferFunction sourceTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22;
NColorManagement::eTransferFunction targetTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22;
STFRange srcTFRange;
STFRange dstTFRange;
float srcRefLuminance = 80;
float dstRefLuminance = 80;
std::array<std::array<double, 3>, 3> convertMatrix;
bool needsTonemap = false;
float maxLuminance = 80;
float dstMaxLuminance = 80;
std::array<std::array<double, 3>, 3> dstPrimaries2XYZ;
bool needsSDRmod = false;
float sdrSaturation = 1.0;
float sdrBrightnessMultiplier = 1.0;
};
class CHyprRenderer {
public:
CHyprRenderer();
@ -89,6 +124,9 @@ class CHyprRenderer {
void renderSnapshot(PHLLS);
void renderSnapshot(WP<Desktop::View::CPopup>);
//
NColorManagement::PImageDescription workBufferImageDescription();
// if RENDER_MODE_NORMAL, provided damage will be written to.
// otherwise, it will be the one used.
bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP<IHLBuffer> buffer = {}, CFramebuffer* fb = nullptr, bool simple = false);
@ -121,6 +159,10 @@ class CHyprRenderer {
CRenderPass m_renderPass = {};
SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription,
SP<CWLSurfaceResource> surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1);
bool reloadShaders(const std::string& path = "");
private:
void arrangeLayerArray(PHLMONITOR, const std::vector<PHLLSREF>&, bool, CBox*);
void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry);

View file

@ -139,11 +139,13 @@ void CShader::getUniformLocations() {
m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation");
m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier");
m_uniformLocations[SHADER_CONVERT_MATRIX] = getUniform("convertMatrix");
m_uniformLocations[SHADER_USE_ICC] = getUniform("useIcc");
m_uniformLocations[SHADER_LUT_3D] = getUniform("iccLut3D");
m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize");
//
m_uniformLocations[SHADER_TEX] = getUniform("tex");
m_uniformLocations[SHADER_BLURRED_BG] = getUniform("blurredBG");
m_uniformLocations[SHADER_UV_SIZE] = getUniform("uvSize");
m_uniformLocations[SHADER_UV_OFFSET] = getUniform("uvOffset");
m_uniformLocations[SHADER_ALPHA] = getUniform("alpha");
m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos");
m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord");

View file

@ -74,9 +74,11 @@ enum eShaderUniform : uint8_t {
SHADER_POINTER_INACTIVE_TIMEOUT,
SHADER_POINTER_LAST_ACTIVE,
SHADER_POINTER_SIZE,
SHADER_USE_ICC,
SHADER_LUT_3D,
SHADER_LUT_SIZE,
SHADER_BLURRED_BG,
SHADER_UV_SIZE,
SHADER_UV_OFFSET,
SHADER_LAST,
};

176
src/render/ShaderLoader.cpp Normal file
View file

@ -0,0 +1,176 @@
#include "ShaderLoader.hpp"
#include <format>
#include <hyprutils/memory/Casts.hpp>
#include <hyprutils/memory/UniquePtr.hpp>
#include <hyprutils/string/String.hpp>
#include <hyprutils/path/Path.hpp>
#include "../debug/log/Logger.hpp"
#include "shaders/Shaders.hpp"
#include "../helpers/fs/FsUtils.hpp"
#include "Renderer.hpp"
#include <glslang/Public/resource_limits_c.h>
#include <string>
#include <filesystem>
using namespace Render;
CShaderLoader::CShaderLoader(const std::vector<std::string> includes, const std::array<std::string, SH_FRAG_LAST>& frags, const std::string shaderPath) : m_shaderPath(shaderPath) {
m_callbacks = glsl_include_callbacks_t{
.include_local =
[](void* ctx, const char* header_name, const char* includer_name, size_t include_depth) {
auto shaderLoader = sc<CShaderLoader*>(ctx);
auto res = new glsl_include_result_t;
if (shaderLoader->m_overrideDefines.length() && std::string{header_name} == "defines.h") {
res->header_name = header_name;
res->header_data = shaderLoader->m_overrideDefines.c_str();
res->header_length = shaderLoader->m_overrideDefines.length();
} else if (shaderLoader->includes().contains(header_name)) {
res->header_name = header_name;
res->header_data = shaderLoader->includes().at(header_name).c_str();
res->header_length = shaderLoader->includes().at(header_name).length();
} else {
res->header_name = nullptr;
res->header_data = nullptr;
res->header_length = 0;
}
shaderLoader->m_includeResults.push_back(res);
return res;
},
.free_include_result =
[](void* ctx, glsl_include_result_t* result) {
auto shaderLoader = sc<CShaderLoader*>(ctx);
std::erase(shaderLoader->m_includeResults, result);
delete result;
return 0;
},
};
for (const auto& inc : includes) {
include(inc);
}
std::ranges::transform(frags, m_fragFiles.begin(), [&](const auto& filename) { return loadShader(filename); });
}
CShaderLoader::~CShaderLoader() {
// glslFreeIncludeResult should leave it empty by this point
for (const auto& res : m_includeResults) {
delete res;
}
}
void CShaderLoader::include(const std::string& filename) {
m_includes.insert({filename, loadShader(filename)});
}
std::string CShaderLoader::getDefines(ShaderFeatureFlags features) {
std::string res = "";
std::map<std::string, std::string> defines = {
{"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"},
{"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"},
{"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"},
};
for (const auto& [name, value] : defines) {
res += std::format("#define {} {}\n", name, value);
}
return res;
}
std::string CShaderLoader::processSource(const std::string& source, glslang_stage_t stage) {
const glslang_input_t input = {
.language = GLSLANG_SOURCE_GLSL,
.stage = stage,
.client = GLSLANG_CLIENT_NONE,
.target_language = GLSLANG_TARGET_NONE,
.code = source.c_str(),
.default_version = 100,
.default_profile = GLSLANG_NO_PROFILE,
.force_default_version_and_profile = false,
.forward_compatible = false,
.messages = GLSLANG_MSG_DEFAULT_BIT,
.resource = glslang_default_resource(),
.callbacks = m_callbacks,
.callbacks_ctx = this,
};
glslang_shader_t* shader = glslang_shader_create(&input);
if (!glslang_shader_preprocess(shader, &input)) {
Log::logger->log(Log::ERR, "GLSL preprocessing failed");
Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_log(shader));
Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_debug_log(shader));
Log::logger->log(Log::ERR, "{}", input.code);
glslang_shader_delete(shader);
return source;
}
std::stringstream stream(glslang_shader_get_preprocessed_code(shader));
std::string code = "";
std::string line;
while (std::getline(stream, line)) {
if (!line.starts_with("#line "))
code += line + "\n";
}
return code;
}
std::string CShaderLoader::process(const std::string& filename) {
auto source = loadShader(filename);
return processSource(source, filename.ends_with(".vert") ? GLSLANG_STAGE_VERTEX : GLSLANG_STAGE_FRAGMENT);
}
std::string CShaderLoader::process(const std::string& filename, const std::map<std::string, std::string>& defines) {
m_overrideDefines = "";
for (const auto& [name, value] : defines) {
m_overrideDefines += std::format("#define {} {}\n", name, value);
}
const auto& res = process(filename);
m_overrideDefines = "";
return res;
}
std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features) {
static const auto PCM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
if (!*PCM)
features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD);
if (!m_fragVariants[frag].contains(features)) {
ASSERT(m_fragFiles[frag].length());
m_overrideDefines = getDefines(features);
m_fragVariants[frag][features] = processSource(m_fragFiles[frag]);
m_overrideDefines = "";
}
return m_fragVariants[frag][features];
}
const std::map<std::string, std::string>& CShaderLoader::includes() {
return m_includes;
}
// TODO notify user if bundled shader is newer than ~/.config override
std::string CShaderLoader::loadShader(const std::string& filename) {
if (m_shaderPath.length()) {
std::filesystem::path path = m_shaderPath;
const auto src = NFsUtils::readFileAsString(path / filename);
if (src.has_value())
return src.value();
}
const auto home = Hyprutils::Path::getHome();
if (home.has_value()) {
const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename);
if (src.has_value())
return src.value();
}
for (auto& e : ASSET_PATHS) {
const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename);
if (src.has_value())
return src.value();
}
if (SHADERS.contains(filename))
return SHADERS.at(filename);
throw std::runtime_error(std::format("Couldn't load shader {}", filename));
}

View file

@ -0,0 +1,77 @@
#pragma once
#include <array>
#include <glslang/Include/glslang_c_interface.h>
#include <string>
#include <vector>
#include <map>
#include "../helpers/memory/Memory.hpp"
namespace Render {
enum ePreparedFragmentShaderFeature : uint16_t {
SH_FEAT_UNKNOWN = 0, // all features just in case
SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling
SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling
SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint
SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0
SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM
SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01
SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1)
SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend
SH_FEAT_ICC = (1 << 8), //
// uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD
};
using ShaderFeatureFlags = uint16_t;
enum ePreparedFragmentShader : uint8_t {
SH_FRAG_QUAD = 0,
SH_FRAG_PASSTHRURGBA,
SH_FRAG_MATTE,
SH_FRAG_EXT,
SH_FRAG_BLUR1,
SH_FRAG_BLUR2,
SH_FRAG_BLURPREPARE,
SH_FRAG_BLURFINISH,
SH_FRAG_SHADOW,
SH_FRAG_SURFACE,
SH_FRAG_BORDER1,
SH_FRAG_GLITCH,
SH_FRAG_LAST,
};
class CShaderLoader {
public:
CShaderLoader(const std::vector<std::string> includes, const std::array<std::string, SH_FRAG_LAST>& frags, const std::string shaderPath = "");
~CShaderLoader();
void include(const std::string& filename);
std::string process(const std::string& filename);
std::string process(const std::string& filename, const std::map<std::string, std::string>& defines);
std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features);
const std::map<std::string, std::string>& includes();
std::vector<glsl_include_result_t*> m_includeResults;
private:
std::string loadShader(const std::string& filename);
std::string getDefines(ShaderFeatureFlags features);
std::string processSource(const std::string& source, glslang_stage_t stage = GLSLANG_STAGE_FRAGMENT);
//
std::string m_shaderPath;
std::array<std::string, SH_FRAG_LAST> m_fragFiles;
std::array<std::map<ShaderFeatureFlags, std::string>, SH_FRAG_LAST> m_fragVariants;
std::map<std::string, std::string> m_includes;
std::string m_overrideDefines;
glsl_include_callbacks_t m_callbacks;
};
inline UP<CShaderLoader> g_pShaderLoader;
}

View file

@ -1,306 +1,27 @@
uniform vec2 srcTFRange;
uniform vec2 dstTFRange;
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#include "cm_helpers.glsl"
uniform vec2 srcTFRange;
uniform vec2 dstTFRange;
uniform float srcRefLuminance;
uniform mat3 convertMatrix;
uniform mat3 convertMatrix;
#include "sdr_mod.glsl"
uniform int useIcc;
#if USE_ICC
uniform highp sampler3D iccLut3D;
uniform float iccLutSize;
uniform float iccLutSize;
#endif
//enum eTransferFunction
#define CM_TRANSFER_FUNCTION_BT1886 1
#define CM_TRANSFER_FUNCTION_GAMMA22 2
#define CM_TRANSFER_FUNCTION_GAMMA28 3
#define CM_TRANSFER_FUNCTION_ST240 4
#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5
#define CM_TRANSFER_FUNCTION_LOG_100 6
#define CM_TRANSFER_FUNCTION_LOG_316 7
#define CM_TRANSFER_FUNCTION_XVYCC 8
#define CM_TRANSFER_FUNCTION_SRGB 9
#define CM_TRANSFER_FUNCTION_EXT_SRGB 10
#define CM_TRANSFER_FUNCTION_ST2084_PQ 11
#define CM_TRANSFER_FUNCTION_ST428 12
#define CM_TRANSFER_FUNCTION_HLG 13
#if USE_SDR_MOD
uniform float sdrSaturation;
uniform float sdrBrightnessMultiplier;
#endif
// sRGB constants
#define SRGB_POW 2.4
#define SRGB_CUT 0.0031308
#define SRGB_SCALE 12.92
#define SRGB_ALPHA 1.055
#define BT1886_POW (1.0 / 0.45)
#define BT1886_CUT 0.018053968510807
#define BT1886_SCALE 4.5
#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT)
// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf
#define ST240_POW (1.0 / 0.45)
#define ST240_CUT 0.0228
#define ST240_SCALE 4.0
#define ST240_ALPHA 1.1115
#define ST428_POW 2.6
#define ST428_SCALE (52.37 / 48.0)
// PQ constants
#define PQ_M1 0.1593017578125
#define PQ_M2 78.84375
#define PQ_INV_M1 (1.0 / PQ_M1)
#define PQ_INV_M2 (1.0 / PQ_M2)
#define PQ_C1 0.8359375
#define PQ_C2 18.8515625
#define PQ_C3 18.6875
// HLG constants
#define HLG_D_CUT (1.0 / 12.0)
#define HLG_E_CUT 0.5
#define HLG_A 0.17883277
#define HLG_B 0.28466892
#define HLG_C 0.55991073
#define SDR_MIN_LUMINANCE 0.2
#define SDR_MAX_LUMINANCE 80.0
#define HDR_MIN_LUMINANCE 0.005
#define HDR_MAX_LUMINANCE 10000.0
#define HLG_MAX_LUMINANCE 1000.0
#define M_E 2.718281828459045
vec3 applyIcc3DLut(vec3 linearRgb01) {
vec3 x = clamp(linearRgb01, 0.0, 1.0);
// Map [0..1] to texel centers to avoid edge issues
float N = iccLutSize;
vec3 coord = (x * (N - 1.0) + 0.5) / N;
return texture(iccLut3D, coord).rgb;
}
vec3 xy2xyz(vec2 xy) {
if (xy.y == 0.0)
return vec3(0.0, 0.0, 0.0);
return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y);
}
// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf
vec3 tfInvPQ(vec3 color) {
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2));
return pow(
(max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E),
vec3(PQ_INV_M1)
);
}
vec3 tfInvHLG(vec3 color) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT));
vec3 lo = color.rgb * color.rgb / 3.0;
vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0;
return mix(hi, lo, isLow);
}
// Many transfer functions (including sRGB) follow the same pattern: a linear
// segment for small values and a power function for larger values. The
// following function implements this pattern from which sRGB, BT.1886, and
// others can be derived by plugging in the right constants.
vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale));
vec3 lo = color.rgb / scale;
vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma));
return mix(hi, lo, isLow);
}
vec3 tfInvSRGB(vec3 color) {
return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA);
}
vec3 tfInvExtSRGB(vec3 color) {
// EXT sRGB is the sRGB transfer function mirrored around 0.
return sign(color) * tfInvSRGB(abs(color));
}
vec3 tfInvBT1886(vec3 color) {
return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA);
}
vec3 tfInvXVYCC(vec3 color) {
// The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0,
// same as what EXT sRGB is to sRGB.
return sign(color) * tfInvBT1886(abs(color));
}
vec3 tfInvST240(vec3 color) {
return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA);
}
// Forward transfer functions corresponding to the inverse functions above.
vec3 tfPQ(vec3 color) {
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1));
return pow(
(vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E),
vec3(PQ_M2)
);
}
vec3 tfHLG(vec3 color) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT));
vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0);
vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C;
return mix(hi, lo, isLow);
}
vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(thres));
vec3 lo = color.rgb * scale;
vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0);
return mix(hi, lo, isLow);
}
vec3 tfSRGB(vec3 color) {
return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA);
}
vec3 tfExtSRGB(vec3 color) {
// EXT sRGB is the sRGB transfer function mirrored around 0.
return sign(color) * tfSRGB(abs(color));
}
vec3 tfBT1886(vec3 color) {
return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA);
}
vec3 tfXVYCC(vec3 color) {
// The transfer function for XVYCC is the BT1886 transfer function mirrored around 0,
// same as what EXT sRGB is to sRGB.
return sign(color) * tfBT1886(abs(color));
}
vec3 tfST240(vec3 color) {
return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA);
}
vec3 toLinearRGB(vec3 color, int tf) {
switch (tf) {
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
return color;
case CM_TRANSFER_FUNCTION_ST2084_PQ:
return tfInvPQ(color);
case CM_TRANSFER_FUNCTION_GAMMA22:
return pow(max(color, vec3(0.0)), vec3(2.2));
case CM_TRANSFER_FUNCTION_GAMMA28:
return pow(max(color, vec3(0.0)), vec3(2.8));
case CM_TRANSFER_FUNCTION_HLG:
return tfInvHLG(color);
case CM_TRANSFER_FUNCTION_EXT_SRGB:
return tfInvExtSRGB(color);
case CM_TRANSFER_FUNCTION_BT1886:
return tfInvBT1886(color);
case CM_TRANSFER_FUNCTION_ST240:
return tfInvST240(color);
case CM_TRANSFER_FUNCTION_LOG_100:
return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0)));
case CM_TRANSFER_FUNCTION_LOG_316:
return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0)));
case CM_TRANSFER_FUNCTION_XVYCC:
return tfInvXVYCC(color);
case CM_TRANSFER_FUNCTION_ST428:
return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE;
case CM_TRANSFER_FUNCTION_SRGB:
default:
return tfInvSRGB(color);
}
}
vec4 toLinear(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
color.rgb = toLinearRGB(color.rgb, tf);
color.rgb *= color.a;
return color;
}
vec4 toNit(vec4 color, vec2 range) {
color.rgb = color.rgb * (range[1] - range[0]) + range[0];
return color;
}
vec3 fromLinearRGB(vec3 color, int tf) {
switch (tf) {
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
return color;
case CM_TRANSFER_FUNCTION_ST2084_PQ:
return tfPQ(color);
case CM_TRANSFER_FUNCTION_GAMMA22:
return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2));
case CM_TRANSFER_FUNCTION_GAMMA28:
return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8));
case CM_TRANSFER_FUNCTION_HLG:
return tfHLG(color);
case CM_TRANSFER_FUNCTION_EXT_SRGB:
return tfExtSRGB(color);
case CM_TRANSFER_FUNCTION_BT1886:
return tfBT1886(color);
case CM_TRANSFER_FUNCTION_ST240:
return tfST240(color);
case CM_TRANSFER_FUNCTION_LOG_100:
return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01)));
case CM_TRANSFER_FUNCTION_LOG_316:
return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0)));
case CM_TRANSFER_FUNCTION_XVYCC:
return tfXVYCC(color);
case CM_TRANSFER_FUNCTION_ST428:
return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW));
case CM_TRANSFER_FUNCTION_SRGB:
default:
return tfSRGB(color);
}
}
vec4 fromLinear(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
color.rgb = fromLinearRGB(color.rgb, tf);
color.rgb *= color.a;
return color;
}
vec4 fromLinearNit(vec4 color, int tf, vec2 range) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
color.rgb = color.rgb / SDR_MAX_LUMINANCE;
else {
color.rgb /= max(color.a, 0.001);
color.rgb = (color.rgb - range[0]) / (range[1] - range[0]);
color.rgb = fromLinearRGB(color.rgb, tf);
color.rgb *= color.a;
}
return color;
}
#include "tonemap.glsl"
vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 dstxyz) {
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF);
if (useIcc == 1) {
pixColor.rgb = applyIcc3DLut(pixColor.rgb);
pixColor.rgb *= pixColor.a;
} else {
pixColor.rgb = convertMatrix * pixColor.rgb;
pixColor = toNit(pixColor, srcTFRange);
pixColor.rgb *= pixColor.a;
#include "do_tonemap.glsl"
pixColor = fromLinearNit(pixColor, dstTF, dstTFRange);
#include "do_sdr_mod.glsl"
}
return pixColor;
}
#if USE_TONEMAP
uniform float maxLuminance;
uniform float dstMaxLuminance;
uniform float dstRefLuminance;
#endif

View file

@ -1,36 +0,0 @@
#version 300 es
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float contrast;
uniform float brightness;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
#include "CM.glsl"
#include "gain.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) {
pixColor.rgb /= sdrBrightnessMultiplier;
}
pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF);
pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance));
pixColor = fromLinearNit(pixColor, targetTF, dstTFRange);
// contrast
if (contrast != 1.0)
pixColor.rgb = gain(pixColor.rgb, contrast);
// brightness
pixColor.rgb *= max(1.0, brightness);
fragColor = pixColor;
}

View file

@ -1,98 +0,0 @@
#version 300 es
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat3 targetPrimariesXYZ;
uniform vec2 fullSizeUntransformed;
uniform float radiusOuter;
uniform float thick;
// Gradients are in OkLabA!!!! {l, a, b, alpha}
uniform vec4 gradient[10];
uniform vec4 gradient2[10];
uniform int gradientLength;
uniform int gradient2Length;
uniform float angle;
uniform float angle2;
uniform float gradientLerp;
uniform float alpha;
#include "rounding.glsl"
#include "CM.glsl"
#include "border.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
highp vec2 pixCoord = vec2(gl_FragCoord);
highp vec2 pixCoordOuter = pixCoord;
highp vec2 originalPixCoord = v_texcoord;
originalPixCoord *= fullSizeUntransformed;
float additionalAlpha = 1.0;
vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0);
bool done = false;
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoordOuter = pixCoord;
pixCoord -= fullSize * 0.5 - radius;
pixCoordOuter -= fullSize * 0.5 - radiusOuter;
// center the pixes don't make it top-left
pixCoord += vec2(1.0, 1.0) / fullSize;
pixCoordOuter += vec2(1.0, 1.0) / fullSize;
if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) {
float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower);
float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower);
float h = (thick / 2.0);
if (dist < radius - h) {
// lower
float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) {
// higher
float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (distOuter < radiusOuter - h) {
additionalAlpha = 1.0;
done = true;
}
}
// now check for other shit
if (!done) {
// distance to all straight bb borders
float distanceT = originalPixCoord[1];
float distanceB = fullSizeUntransformed[1] - originalPixCoord[1];
float distanceL = originalPixCoord[0];
float distanceR = fullSizeUntransformed[0] - originalPixCoord[0];
// get the smallest
float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR));
if (smallest > thick)
discard;
}
if (additionalAlpha == 0.0)
discard;
pixColor = getColorForCoord(v_texcoord);
pixColor.rgb *= pixColor[3];
pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ);
pixColor *= alpha * additionalAlpha;
fragColor = pixColor;
}

View file

@ -1,143 +1,21 @@
#version 300 es
precision highp float;
uniform sampler2D tex;
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
uniform float radius;
uniform vec2 halfpixel;
uniform int passes;
uniform float vibrancy;
uniform float vibrancy_darkness;
precision highp float;
uniform sampler2D tex;
in vec2 v_texcoord;
// see http://alienryderflex.com/hsp.html
const float Pr = 0.299;
const float Pg = 0.587;
const float Pb = 0.114;
// Y is "v" ( brightness ). X is "s" ( saturation )
// see https://www.desmos.com/3d/a88652b9a4
// Determines if high brightness or high saturation is more important
const float a = 0.93;
const float b = 0.11;
const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors
//
// http://www.flong.com/archive/texts/code/shapers_circ/
float doubleCircleSigmoid(float x, float a) {
a = clamp(a, 0.0, 1.0);
float y = .0;
if (x <= a) {
y = a - sqrt(a * a - x * x);
} else {
y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.));
}
return y;
}
vec3 rgb2hsl(vec3 col) {
float red = col.r;
float green = col.g;
float blue = col.b;
float minc = min(col.r, min(col.g, col.b));
float maxc = max(col.r, max(col.g, col.b));
float delta = maxc - minc;
float lum = (minc + maxc) * 0.5;
float sat = 0.0;
float hue = 0.0;
if (lum > 0.0 && lum < 1.0) {
float mul = (lum < 0.5) ? (lum) : (1.0 - lum);
sat = delta / (mul * 2.0);
}
if (delta > 0.0) {
vec3 maxcVec = vec3(maxc);
vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red)));
vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta;
hue += dot(adds, masks);
hue /= 6.0;
if (hue < 0.0)
hue += 1.0;
}
return vec3(hue, sat, lum);
}
vec3 hsl2rgb(vec3 col) {
const float onethird = 1.0 / 3.0;
const float twothird = 2.0 / 3.0;
const float rcpsixth = 6.0;
float hue = col.x;
float sat = col.y;
float lum = col.z;
vec3 xt = vec3(0.0);
if (hue < onethird) {
xt.r = rcpsixth * (onethird - hue);
xt.g = rcpsixth * hue;
xt.b = 0.0;
} else if (hue < twothird) {
xt.r = 0.0;
xt.g = rcpsixth * (twothird - hue);
xt.b = rcpsixth * (hue - onethird);
} else
xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue));
xt = min(xt, 1.0);
float sat2 = 2.0 * sat;
float satinv = 1.0 - sat;
float luminv = 1.0 - lum;
float lum2m1 = (2.0 * lum) - 1.0;
vec3 ct = (sat2 * xt) + satinv;
vec3 rgb;
if (lum >= 0.5)
rgb = (luminv * ct) + lum2m1;
else
rgb = lum * ct;
return rgb;
}
uniform float radius;
uniform vec2 halfpixel;
uniform int passes;
uniform float vibrancy;
uniform float vibrancy_darkness;
in vec2 v_texcoord;
layout(location = 0) out vec4 fragColor;
#include "blur1.glsl"
void main() {
vec2 uv = v_texcoord * 2.0;
vec4 sum = texture(tex, uv) * 4.0;
sum += texture(tex, uv - halfpixel.xy * radius);
sum += texture(tex, uv + halfpixel.xy * radius);
sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius);
sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius);
vec4 color = sum / 8.0;
if (vibrancy == 0.0) {
fragColor = color;
} else {
// Invert it so that it correctly maps to the config setting
float vibrancy_darkness1 = 1.0 - vibrancy_darkness;
// Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest.
vec3 hsl = rgb2hsl(color.rgb);
// Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow
float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1);
float b1 = b * vibrancy_darkness1;
float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0;
float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0);
vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2]));
fragColor = vec4(newColor, color[3]);
}
fragColor = blur1(v_texcoord, tex, radius, halfpixel, passes, vibrancy, vibrancy_darkness);
}

View file

@ -0,0 +1,130 @@
// see http://alienryderflex.com/hsp.html
const float Pr = 0.299;
const float Pg = 0.587;
const float Pb = 0.114;
// Y is "v" ( brightness ). X is "s" ( saturation )
// see https://www.desmos.com/3d/a88652b9a4
// Determines if high brightness or high saturation is more important
const float a = 0.93;
const float b = 0.11;
const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors
//
// http://www.flong.com/archive/texts/code/shapers_circ/
float doubleCircleSigmoid(float x, float a) {
a = clamp(a, 0.0, 1.0);
float y = .0;
if (x <= a) {
y = a - sqrt(a * a - x * x);
} else {
y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.));
}
return y;
}
vec3 rgb2hsl(vec3 col) {
float red = col.r;
float green = col.g;
float blue = col.b;
float minc = min(col.r, min(col.g, col.b));
float maxc = max(col.r, max(col.g, col.b));
float delta = maxc - minc;
float lum = (minc + maxc) * 0.5;
float sat = 0.0;
float hue = 0.0;
if (lum > 0.0 && lum < 1.0) {
float mul = (lum < 0.5) ? (lum) : (1.0 - lum);
sat = delta / (mul * 2.0);
}
if (delta > 0.0) {
vec3 maxcVec = vec3(maxc);
vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red)));
vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta;
hue += dot(adds, masks);
hue /= 6.0;
if (hue < 0.0)
hue += 1.0;
}
return vec3(hue, sat, lum);
}
vec3 hsl2rgb(vec3 col) {
const float onethird = 1.0 / 3.0;
const float twothird = 2.0 / 3.0;
const float rcpsixth = 6.0;
float hue = col.x;
float sat = col.y;
float lum = col.z;
vec3 xt = vec3(0.0);
if (hue < onethird) {
xt.r = rcpsixth * (onethird - hue);
xt.g = rcpsixth * hue;
xt.b = 0.0;
} else if (hue < twothird) {
xt.r = 0.0;
xt.g = rcpsixth * (twothird - hue);
xt.b = rcpsixth * (hue - onethird);
} else
xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue));
xt = min(xt, 1.0);
float sat2 = 2.0 * sat;
float satinv = 1.0 - sat;
float luminv = 1.0 - lum;
float lum2m1 = (2.0 * lum) - 1.0;
vec3 ct = (sat2 * xt) + satinv;
vec3 rgb;
if (lum >= 0.5)
rgb = (luminv * ct) + lum2m1;
else
rgb = lum * ct;
return rgb;
}
vec4 blur1(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel, int passes, float vibrancy, float vibrancy_darkness) {
vec2 uv = v_texcoord * 2.0;
vec4 sum = texture(tex, uv) * 4.0;
sum += texture(tex, uv - halfpixel.xy * radius);
sum += texture(tex, uv + halfpixel.xy * radius);
sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius);
sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius);
vec4 color = sum / 8.0;
if (vibrancy == 0.0) {
return color;
} else {
// Invert it so that it correctly maps to the config setting
float vibrancy_darkness1 = 1.0 - vibrancy_darkness;
// Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest.
vec3 hsl = rgb2hsl(color.rgb);
// Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow
float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1);
float b1 = b * vibrancy_darkness1;
float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0;
float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0);
vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2]));
return vec4(newColor, color[3]);
}
}

View file

@ -1,25 +1,18 @@
#version 300 es
precision highp float;
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
uniform sampler2D tex;
uniform float radius;
uniform vec2 halfpixel;
uniform float radius;
uniform vec2 halfpixel;
in vec2 v_texcoord;
in vec2 v_texcoord;
layout(location = 0) out vec4 fragColor;
#include "blur2.glsl"
void main() {
vec2 uv = v_texcoord / 2.0;
vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius);
sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius);
sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius);
sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0;
sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius);
sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0;
fragColor = sum / 12.0;
fragColor = blur2(v_texcoord, tex, radius, halfpixel);
}

View file

@ -0,0 +1,15 @@
vec4 blur2(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel) {
vec2 uv = v_texcoord / 2.0;
vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius);
sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius);
sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius);
sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0;
sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius);
sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0;
return sum / 12.0;
}

View file

@ -0,0 +1,17 @@
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness) {
// noise
float noiseHash = hash(v_texcoord);
float noiseAmount = noiseHash - 0.5;
pixColor.rgb += noiseAmount * noise;
// brightness
pixColor.rgb *= min(1.0, brightness);
return pixColor;
}

View file

@ -1,30 +1,19 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord; // is in 0-1
precision highp float;
in vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float noise;
uniform float brightness;
uniform float noise;
uniform float brightness;
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
#include "blurFinish.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
// noise
float noiseHash = hash(v_texcoord);
float noiseAmount = noiseHash - 0.5;
pixColor.rgb += noiseAmount * noise;
// brightness
pixColor.rgb *= min(1.0, brightness);
fragColor = pixColor;
fragColor = blurFinish(pixColor, v_texcoord, noise, brightness);
}

View file

@ -1,26 +1,38 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord; // is in 0-1
#include "defines.h"
precision highp float;
in vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float contrast;
uniform float brightness;
uniform float contrast;
uniform float brightness;
#include "CM.glsl"
#include "gain.glsl"
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
#if USE_CM
uniform vec2 srcTFRange;
uniform vec2 dstTFRange;
uniform float srcRefLuminance;
uniform mat3 convertMatrix;
uniform float sdrBrightnessMultiplier;
#include "cm_helpers.glsl"
#endif
#include "blurprepare.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
// contrast
if (contrast != 1.0)
pixColor.rgb = gain(pixColor.rgb, contrast);
// brightness
pixColor.rgb *= max(1.0, brightness);
fragColor = pixColor;
fragColor = fragColor = blurPrepare(texture(tex, v_texcoord), contrast, brightness
#if USE_CM
,
sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange, srcRefLuminance, sdrBrightnessMultiplier
#endif
);
}

View file

@ -0,0 +1,37 @@
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#include "defines.h"
#if USE_CM
#include "cm_helpers.glsl"
#endif
#include "gain.glsl"
vec4 blurPrepare(vec4 pixColor, float contrast, float brightness
#if USE_CM
,
int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange, float srcRefLuminance, float sdrBrightnessMultiplier
#endif
) {
#if USE_CM
if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) {
pixColor.rgb /= sdrBrightnessMultiplier;
}
pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF);
pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance));
pixColor = fromLinearNit(pixColor, targetTF, dstTFRange);
#endif
// contrast
if (contrast != 1.0)
pixColor.rgb = gain(pixColor.rgb, contrast);
// brightness
pixColor.rgb *= max(1.0, brightness);
return pixColor;
}

View file

@ -1,92 +1,60 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord;
precision highp float;
in vec2 v_texcoord;
uniform vec2 fullSizeUntransformed;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat3 targetPrimariesXYZ;
uniform vec2 fullSizeUntransformed;
uniform float radiusOuter;
uniform float thick;
// Gradients are in OkLabA!!!! {l, a, b, alpha}
uniform vec4 gradient[10];
uniform vec4 gradient2[10];
uniform int gradientLength;
uniform int gradient2Length;
uniform vec4 gradient[10];
uniform vec4 gradient2[10];
uniform int gradientLength;
uniform int gradient2Length;
uniform float angle;
uniform float angle2;
uniform float gradientLerp;
uniform float alpha;
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl"
#include "CM.glsl"
#include "border.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
highp vec2 pixCoord = vec2(gl_FragCoord);
highp vec2 pixCoordOuter = pixCoord;
highp vec2 originalPixCoord = v_texcoord;
originalPixCoord *= fullSizeUntransformed;
float additionalAlpha = 1.0;
vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0);
bool done = false;
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoordOuter = pixCoord;
pixCoord -= fullSize * 0.5 - radius;
pixCoordOuter -= fullSize * 0.5 - radiusOuter;
// center the pixes don't make it top-left
pixCoord += vec2(1.0, 1.0) / fullSize;
pixCoordOuter += vec2(1.0, 1.0) / fullSize;
if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) {
float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower);
float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower);
float h = (thick / 2.0);
if (dist < radius - h) {
// lower
float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) {
// higher
float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (distOuter < radiusOuter - h) {
additionalAlpha = 1.0;
done = true;
}
}
// now check for other shit
if (!done) {
// distance to all straight bb borders
float distanceT = originalPixCoord[1];
float distanceB = fullSizeUntransformed[1] - originalPixCoord[1];
float distanceL = originalPixCoord[0];
float distanceR = fullSizeUntransformed[0] - originalPixCoord[0];
// get the smallest
float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR));
if (smallest > thick)
discard;
}
if (additionalAlpha == 0.0)
discard;
pixColor = getColorForCoord(v_texcoord);
pixColor.rgb *= pixColor[3];
pixColor *= alpha * additionalAlpha;
fragColor = pixColor;
fragColor = getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length,
gradient2, angle2, gradientLerp
#if USE_CM
,
sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
#if USE_ICC
,
iccLut3D, iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
#endif
#if USE_SDR_MOD
,
sdrSaturation, sdrBrightnessMultiplier
#endif
#endif
#endif
);
}

View file

@ -1,18 +1,24 @@
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#include "cm_helpers.glsl"
#if USE_ROUNDING
#include "rounding.glsl"
#endif
vec4 okLabAToSrgb(vec4 lab) {
float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0);
float m = pow(lab[0] + lab[1] * (-0.1055613458) + lab[2] * (-0.0638541728), 3.0);
float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0);
return vec4(fromLinearRGB(
vec3(
l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292,
l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965),
l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010
), CM_TRANSFER_FUNCTION_GAMMA22
), lab[3]);
return vec4(fromLinearRGB(vec3(l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965),
l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010),
CM_TRANSFER_FUNCTION_GAMMA22),
lab[3]);
}
vec4 getOkColorForCoordArray1(vec2 normalizedCoord) {
vec4 getOkColorForCoordArray1(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle) {
if (gradientLength < 2)
return gradient[0];
@ -20,14 +26,14 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord) {
if (angle > 4.71 /* 270 deg */) {
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = 6.28 - angle;
finalAng = 6.28 - angle;
} else if (angle > 3.14 /* 180 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = angle - 3.14;
finalAng = angle - 3.14;
} else if (angle > 1.57 /* 90 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
finalAng = 3.14 - angle;
finalAng = 3.14 - angle;
} else {
finalAng = angle;
}
@ -35,13 +41,13 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord) {
float sine = sin(finalAng);
float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1);
int bottom = int(floor(progress));
int top = bottom + 1;
int bottom = int(floor(progress));
int top = bottom + 1;
return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress);
}
vec4 getOkColorForCoordArray2(vec2 normalizedCoord) {
vec4 getOkColorForCoordArray2(vec2 normalizedCoord, float angle, int gradient2Length, vec4 gradient2[10], float angle2) {
if (gradient2Length < 2)
return gradient2[0];
@ -49,14 +55,14 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord) {
if (angle2 > 4.71 /* 270 deg */) {
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = 6.28 - angle;
finalAng = 6.28 - angle;
} else if (angle2 > 3.14 /* 180 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = angle - 3.14;
finalAng = angle - 3.14;
} else if (angle2 > 1.57 /* 90 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
finalAng = 3.14 - angle2;
finalAng = 3.14 - angle2;
} else {
finalAng = angle2;
}
@ -64,19 +70,134 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord) {
float sine = sin(finalAng);
float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradient2Length - 1);
int bottom = int(floor(progress));
int top = bottom + 1;
int bottom = int(floor(progress));
int top = bottom + 1;
return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress);
}
vec4 getColorForCoord(vec2 normalizedCoord) {
vec4 result1 = getOkColorForCoordArray1(normalizedCoord);
vec4 getColorForCoord(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp) {
vec4 result1 = getOkColorForCoordArray1(normalizedCoord, gradientLength, gradient, angle);
if (gradient2Length <= 0)
return okLabAToSrgb(result1);
vec4 result2 = getOkColorForCoordArray2(normalizedCoord);
vec4 result2 = getOkColorForCoordArray2(normalizedCoord, angle, gradient2Length, gradient2, angle2);
return okLabAToSrgb(mix(result1, result2, gradientLerp));
}
vec4 getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float radiusOuter, float thick, float radius, float roundingPower, vec2 topLeft, vec2 fullSize,
int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp
#if USE_CM
,
int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange
#if USE_ICC
,
highp sampler3D iccLut3D, float iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
mat3 targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance
#endif
#if USE_SDR_MOD
,
float sdrSaturation, float sdrBrightnessMultiplier
#endif
#endif
#endif
) {
vec2 pixCoord = vec2(gl_FragCoord);
vec2 pixCoordOuter = pixCoord;
vec2 originalPixCoord = v_texcoord;
originalPixCoord *= fullSizeUntransformed;
float additionalAlpha = 1.0;
vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0);
bool done = false;
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoordOuter = pixCoord;
pixCoord -= fullSize * 0.5 - radius;
pixCoordOuter -= fullSize * 0.5 - radiusOuter;
// center the pixes don't make it top-left
pixCoord += vec2(1.0, 1.0) / fullSize;
pixCoordOuter += vec2(1.0, 1.0) / fullSize;
#if USE_ROUNDING
if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) {
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower);
float distOuter = pow(pow(pixCoordOuter.x, roundingPower) + pow(pixCoordOuter.y, roundingPower), 1.0 / roundingPower);
float h = (thick / 2.0);
if (dist < radius - h) {
// lower
float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) {
// higher
float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (distOuter < radiusOuter - h) {
additionalAlpha = 1.0;
done = true;
}
}
#endif
// now check for other shit
if (!done) {
// distance to all straight bb borders
float distanceT = originalPixCoord[1];
float distanceB = fullSizeUntransformed[1] - originalPixCoord[1];
float distanceL = originalPixCoord[0];
float distanceR = fullSizeUntransformed[0] - originalPixCoord[0];
// get the smallest
float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR));
if (smallest > thick)
discard;
}
if (additionalAlpha == 0.0)
discard;
pixColor = getColorForCoord(v_texcoord, gradientLength, gradient, angle, gradient2Length, gradient2, angle2, gradientLerp);
pixColor.rgb *= pixColor[3];
#if USE_CM
pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
#if USE_ICC
,
iccLut3D, iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
#endif
#if USE_SDR_MOD
,
sdrSaturation, sdrBrightnessMultiplier
#endif
#endif
);
#endif
pixColor *= alpha * additionalAlpha;
return pixColor;
}

View file

@ -0,0 +1,248 @@
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#ifndef CM_HELPERS_GLSL
#define CM_HELPERS_GLSL
#include "defines.h"
#include "constants.h"
#if USE_SDR_MOD
vec4 saturate(vec4 color, mat3 primaries, float saturation) {
if (saturation == 1.0)
return color;
vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]);
float Y = dot(color.rgb, brightness);
return vec4(mix(vec3(Y), color.rgb, saturation), color[3]);
}
#endif
vec3 applyIcc3DLut(vec3 linearRgb01, highp sampler3D iccLut3D, float iccLutSize) {
vec3 x = clamp(linearRgb01, 0.0, 1.0);
// Map [0..1] to texel centers to avoid edge issues
float N = iccLutSize;
vec3 coord = (x * (N - 1.0) + 0.5) / N;
return texture(iccLut3D, coord).rgb;
}
vec3 xy2xyz(vec2 xy) {
if (xy.y == 0.0)
return vec3(0.0, 0.0, 0.0);
return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y);
}
// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf
vec3 tfInvPQ(vec3 color) {
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2));
return pow((max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), vec3(PQ_INV_M1));
}
vec3 tfInvHLG(vec3 color) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT));
vec3 lo = color.rgb * color.rgb / 3.0;
vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0;
return mix(hi, lo, isLow);
}
// Many transfer functions (including sRGB) follow the same pattern: a linear
// segment for small values and a power function for larger values. The
// following function implements this pattern from which sRGB, BT.1886, and
// others can be derived by plugging in the right constants.
vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale));
vec3 lo = color.rgb / scale;
vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma));
return mix(hi, lo, isLow);
}
vec3 tfInvSRGB(vec3 color) {
return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA);
}
vec3 tfInvExtSRGB(vec3 color) {
// EXT sRGB is the sRGB transfer function mirrored around 0.
return sign(color) * tfInvSRGB(abs(color));
}
vec3 tfInvBT1886(vec3 color) {
return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA);
}
vec3 tfInvXVYCC(vec3 color) {
// The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0,
// same as what EXT sRGB is to sRGB.
return sign(color) * tfInvBT1886(abs(color));
}
vec3 tfInvST240(vec3 color) {
return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA);
}
// Forward transfer functions corresponding to the inverse functions above.
vec3 tfPQ(vec3 color) {
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1));
return pow((vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), vec3(PQ_M2));
}
vec3 tfHLG(vec3 color) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT));
vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0);
vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C;
return mix(hi, lo, isLow);
}
vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) {
bvec3 isLow = lessThanEqual(color.rgb, vec3(thres));
vec3 lo = color.rgb * scale;
vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0);
return mix(hi, lo, isLow);
}
vec3 tfSRGB(vec3 color) {
return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA);
}
vec3 tfExtSRGB(vec3 color) {
// EXT sRGB is the sRGB transfer function mirrored around 0.
return sign(color) * tfSRGB(abs(color));
}
vec3 tfBT1886(vec3 color) {
return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA);
}
vec3 tfXVYCC(vec3 color) {
// The transfer function for XVYCC is the BT1886 transfer function mirrored around 0,
// same as what EXT sRGB is to sRGB.
return sign(color) * tfBT1886(abs(color));
}
vec3 tfST240(vec3 color) {
return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA);
}
vec3 toLinearRGB(vec3 color, int tf) {
switch (tf) {
case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color;
case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfInvPQ(color);
case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(2.2));
case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(2.8));
case CM_TRANSFER_FUNCTION_HLG: return tfInvHLG(color);
case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfInvExtSRGB(color);
case CM_TRANSFER_FUNCTION_BT1886: return tfInvBT1886(color);
case CM_TRANSFER_FUNCTION_ST240: return tfInvST240(color);
case CM_TRANSFER_FUNCTION_LOG_100: return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0)));
case CM_TRANSFER_FUNCTION_LOG_316: return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0)));
case CM_TRANSFER_FUNCTION_XVYCC: return tfInvXVYCC(color);
case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE;
case CM_TRANSFER_FUNCTION_SRGB:
default: return tfInvSRGB(color);
}
}
vec4 toLinear(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
color.rgb = toLinearRGB(color.rgb, tf);
color.rgb *= color.a;
return color;
}
vec4 toNit(vec4 color, vec2 range) {
color.rgb = color.rgb * (range[1] - range[0]) + range[0];
return color;
}
vec3 fromLinearRGB(vec3 color, int tf) {
switch (tf) {
case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color;
case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfPQ(color);
case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2));
case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8));
case CM_TRANSFER_FUNCTION_HLG: return tfHLG(color);
case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfExtSRGB(color);
case CM_TRANSFER_FUNCTION_BT1886: return tfBT1886(color);
case CM_TRANSFER_FUNCTION_ST240: return tfST240(color);
case CM_TRANSFER_FUNCTION_LOG_100: return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01)));
case CM_TRANSFER_FUNCTION_LOG_316: return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0)));
case CM_TRANSFER_FUNCTION_XVYCC: return tfXVYCC(color);
case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW));
case CM_TRANSFER_FUNCTION_SRGB:
default: return tfSRGB(color);
}
}
vec4 fromLinear(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
color.rgb = fromLinearRGB(color.rgb, tf);
color.rgb *= color.a;
return color;
}
vec4 fromLinearNit(vec4 color, int tf, vec2 range) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
color.rgb = color.rgb / SDR_MAX_LUMINANCE;
else {
color.rgb /= max(color.a, 0.001);
color.rgb = (color.rgb - range[0]) / (range[1] - range[0]);
color.rgb = fromLinearRGB(color.rgb, tf);
color.rgb *= color.a;
}
return color;
}
#if USE_TONEMAP
#include "tonemap.glsl"
#endif
vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange
#if USE_ICC
,
highp sampler3D iccLut3D, float iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
mat3 dstxyz
#endif
#if USE_TONEMAP
,
float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance
#endif
#if USE_SDR_MOD
,
float sdrSaturation, float sdrBrightnessMultiplier
#endif
#endif
) {
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF);
#if USE_ICC
pixColor.rgb = applyIcc3DLut(pixColor.rgb, iccLut3D, iccLutSize);
pixColor.rgb *= pixColor.a;
#else
pixColor.rgb = convertMatrix * pixColor.rgb;
pixColor = toNit(pixColor, srcTFRange);
pixColor.rgb *= pixColor.a;
#if USE_TONEMAP
pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance);
#endif
pixColor = fromLinearNit(pixColor, dstTF, dstTFRange);
#if USE_SDR_MOD
pixColor = saturate(pixColor, dstxyz, sdrSaturation);
pixColor.rgb *= sdrBrightnessMultiplier;
#endif
#endif
return pixColor;
}
#endif

View file

@ -0,0 +1,62 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
//enum eTransferFunction
#define CM_TRANSFER_FUNCTION_BT1886 1
#define CM_TRANSFER_FUNCTION_GAMMA22 2
#define CM_TRANSFER_FUNCTION_GAMMA28 3
#define CM_TRANSFER_FUNCTION_ST240 4
#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5
#define CM_TRANSFER_FUNCTION_LOG_100 6
#define CM_TRANSFER_FUNCTION_LOG_316 7
#define CM_TRANSFER_FUNCTION_XVYCC 8
#define CM_TRANSFER_FUNCTION_SRGB 9
#define CM_TRANSFER_FUNCTION_EXT_SRGB 10
#define CM_TRANSFER_FUNCTION_ST2084_PQ 11
#define CM_TRANSFER_FUNCTION_ST428 12
#define CM_TRANSFER_FUNCTION_HLG 13
// sRGB constants
#define SRGB_POW 2.4
#define SRGB_CUT 0.0031308
#define SRGB_SCALE 12.92
#define SRGB_ALPHA 1.055
#define BT1886_POW (1.0 / 0.45)
#define BT1886_CUT 0.018053968510807
#define BT1886_SCALE 4.5
#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT)
// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf
#define ST240_POW (1.0 / 0.45)
#define ST240_CUT 0.0228
#define ST240_SCALE 4.0
#define ST240_ALPHA 1.1115
#define ST428_POW 2.6
#define ST428_SCALE (52.37 / 48.0)
// PQ constants
#define PQ_M1 0.1593017578125
#define PQ_M2 78.84375
#define PQ_INV_M1 (1.0 / PQ_M1)
#define PQ_INV_M2 (1.0 / PQ_M2)
#define PQ_C1 0.8359375
#define PQ_C2 18.8515625
#define PQ_C3 18.6875
// HLG constants
#define HLG_D_CUT (1.0 / 12.0)
#define HLG_E_CUT 0.5
#define HLG_A 0.17883277
#define HLG_B 0.28466892
#define HLG_C 0.55991073
#define SDR_MIN_LUMINANCE 0.2
#define SDR_MAX_LUMINANCE 80.0
#define HDR_MIN_LUMINANCE 0.005
#define HDR_MAX_LUMINANCE 10000.0
#define HLG_MAX_LUMINANCE 1000.0
#define M_E 2.718281828459045
#endif

View file

@ -0,0 +1,10 @@
// DO NOT EDIT. Will be overwritten in runtime
#define USE_RGBA 1
#define USE_DISCARD 1
#define USE_TINT 1
#define USE_ROUNDING 1
#define USE_CM 1
#define USE_TONEMAP 1
#define USE_SDR_MOD 1
#define USE_BLUR 1
#define USE_ICC 1

View file

@ -1,3 +0,0 @@
uniform bool discardOpaque;
uniform bool discardAlpha;
uniform float discardAlphaValue;

View file

@ -1 +0,0 @@
pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ);

View file

@ -1,5 +0,0 @@
if (discardOpaque && pixColor.a * alpha == 1.0)
discard;
if (discardAlpha && pixColor.a <= discardAlphaValue)
discard;

View file

@ -1 +0,0 @@
pixColor = rounding(pixColor);

View file

@ -1,2 +0,0 @@
pixColor = saturate(pixColor, dstxyz, sdrSaturation);
pixColor.rgb *= sdrBrightnessMultiplier;

View file

@ -1 +0,0 @@
pixColor.rgb = pixColor.rgb * tint;

View file

@ -1 +0,0 @@
pixColor = tonemap(pixColor, dstxyz);

View file

@ -1,20 +1,25 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#extension GL_OES_EGL_image_external_essl3 : require
precision highp float;
in vec2 v_texcoord;
precision highp float;
in vec2 v_texcoord;
uniform samplerExternalOES tex;
uniform float alpha;
uniform float alpha;
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl"
uniform int discardOpaque;
uniform int discardAlpha;
uniform int discardAlphaValue;
uniform int discardOpaque;
uniform int discardAlpha;
uniform int discardAlphaValue;
uniform int applyTint;
uniform int applyTint;
uniform vec3 tint;
layout(location = 0) out vec4 fragColor;
@ -23,16 +28,16 @@ void main() {
vec4 pixColor = texture(tex, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
discard;
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
if (radius > 0.0)
pixColor = rounding(pixColor);
pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize);
fragColor = pixColor * alpha;
}

View file

@ -1 +0,0 @@
#include "get_rgbx_pixel.glsl"

View file

@ -1 +0,0 @@
vec4 pixColor = texture(tex, v_texcoord);

View file

@ -1 +0,0 @@
vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);

View file

@ -1 +0,0 @@
#include "primaries_xyz_uniform.glsl"

View file

@ -1 +0,0 @@
const mat3 targetPrimariesXYZ = mat3(0.0);

View file

@ -1 +0,0 @@
uniform mat3 targetPrimariesXYZ;

View file

@ -1,17 +1,27 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec4 v_color;
#include "defines.h"
precision highp float;
in vec4 v_color;
#if USE_ROUNDING
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl"
#endif
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = v_color;
if (radius > 0.0)
pixColor = rounding(pixColor);
#if USE_ROUNDING
pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize);
#endif
fragColor = pixColor;
}

View file

@ -1,13 +1,10 @@
#ifndef ROUNDING_GLSL
#define ROUNDING_GLSL
// smoothing constant for the edge: more = blurrier, but smoother
#define M_PI 3.1415926535897932384626433832795
#define M_PI 3.1415926535897932384626433832795
#define SMOOTHING_CONSTANT (M_PI / 5.34665792551)
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
vec4 rounding(vec4 color) {
vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 fullSize) {
vec2 pixCoord = vec2(gl_FragCoord);
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
@ -15,7 +12,7 @@ vec4 rounding(vec4 color) {
pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix don't make it top-left
if (pixCoord.x + pixCoord.y > radius) {
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower);
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower);
if (dist > radius + SMOOTHING_CONSTANT)
discard;
@ -27,3 +24,4 @@ vec4 rounding(vec4 color) {
return color;
}
#endif

View file

@ -1,10 +0,0 @@
uniform float sdrSaturation;
uniform float sdrBrightnessMultiplier;
vec4 saturate(vec4 color, mat3 primaries, float saturation) {
if (saturation == 1.0)
return color;
vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]);
float Y = dot(color.rgb, brightness);
return vec4(mix(vec3(Y), color.rgb, saturation), color[3]);
}

View file

@ -1,93 +1,57 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec4 v_color;
in vec2 v_texcoord;
#include "defines.h"
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat3 targetPrimariesXYZ;
precision highp float;
in vec4 v_color;
in vec2 v_texcoord;
uniform vec2 topLeft;
uniform vec2 bottomRight;
uniform vec2 fullSize;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat3 targetPrimariesXYZ;
uniform vec2 topLeft;
uniform vec2 bottomRight;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
uniform float range;
uniform float shadowPower;
float pixAlphaRoundedDistance(float distanceToCorner) {
if (distanceToCorner > radius) {
return 0.0;
}
#if USE_CM
#include "cm_helpers.glsl"
#include "CM.glsl"
#endif
if (distanceToCorner > radius - range) {
return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think?
}
return 1.0;
}
float modifiedLength(vec2 a) {
return pow(pow(abs(a.x),roundingPower)+pow(abs(a.y),roundingPower),1.0/roundingPower);
}
#include "shadow.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = v_color;
vec4 pixColor = v_color;
float originalAlpha = pixColor[3];
bool done = false;
vec2 pixCoord = fullSize * v_texcoord;
// ok, now we check the distance to a border.
if (pixCoord[0] < topLeft[0]) {
if (pixCoord[1] < topLeft[1]) {
// top left
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft));
done = true;
} else if (pixCoord[1] > bottomRight[1]) {
// bottom left
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1])));
done = true;
}
} else if (pixCoord[0] > bottomRight[0]) {
if (pixCoord[1] < topLeft[1]) {
// top right
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1])));
done = true;
} else if (pixCoord[1] > bottomRight[1]) {
// bottom right
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight));
done = true;
}
}
if (!done) {
// distance to all straight bb borders
float distanceT = pixCoord[1];
float distanceB = fullSize[1] - pixCoord[1];
float distanceL = pixCoord[0];
float distanceR = fullSize[0] - pixCoord[0];
// get the smallest
float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR));
if (smallest < range) {
pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower);
}
}
if (pixColor[3] == 0.0) {
discard; return;
}
// premultiply
pixColor.rgb *= pixColor[3];
fragColor = pixColor;
fragColor = getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight
#if USE_CM
,
sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
#if USE_ICC
,
iccLut3D, iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
#endif
#if USE_SDR_MOD
,
sdrSaturation, sdrBrightnessMultiplier
#endif
#endif
#endif
);
}

View file

@ -0,0 +1,126 @@
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#ifndef SHADOW_GLSL
#define SHADOW_GLSL
#include "cm_helpers.glsl"
float pixAlphaRoundedDistance(float distanceToCorner, float radius, float range, float shadowPower) {
if (distanceToCorner > radius) {
return 0.0;
}
if (distanceToCorner > radius - range) {
return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think?
}
return 1.0;
}
float modifiedLength(vec2 a, float roundingPower) {
return pow(pow(abs(a.x), roundingPower) + pow(abs(a.y), roundingPower), 1.0 / roundingPower);
}
vec4 getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight
#if USE_CM
,
int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange
#if USE_ICC
,
highp sampler3D iccLut3D, float iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
mat3 targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance
#endif
#if USE_SDR_MOD
,
float sdrSaturation, float sdrBrightnessMultiplier
#endif
#endif
#endif
) {
float originalAlpha = pixColor[3];
bool done = false;
vec2 pixCoord = fullSize * v_texcoord;
// ok, now we check the distance to a border.
if (pixCoord[0] < topLeft[0]) {
if (pixCoord[1] < topLeft[1]) {
// top left
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft, roundingPower), radius, range, shadowPower);
done = true;
} else if (pixCoord[1] > bottomRight[1]) {
// bottom left
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]), roundingPower), radius, range, shadowPower);
done = true;
}
} else if (pixCoord[0] > bottomRight[0]) {
if (pixCoord[1] < topLeft[1]) {
// top right
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]), roundingPower), radius, range, shadowPower);
done = true;
} else if (pixCoord[1] > bottomRight[1]) {
// bottom right
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight, roundingPower), radius, range, shadowPower);
done = true;
}
}
if (!done) {
// distance to all straight bb borders
float distanceT = pixCoord[1];
float distanceB = fullSize[1] - pixCoord[1];
float distanceL = pixCoord[0];
float distanceR = fullSize[0] - pixCoord[0];
// get the smallest
float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR));
if (smallest < range) {
pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower);
}
}
if (pixColor[3] == 0.0) {
discard;
return pixColor;
}
// premultiply
pixColor.rgb *= pixColor[3];
#if USE_CM
pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
#if USE_ICC
,
iccLut3D, iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
#endif
#if USE_SDR_MOD
,
sdrSaturation, sdrBrightnessMultiplier
#endif
#endif
);
#endif
return pixColor;
}
#endif

View file

@ -1,25 +1,104 @@
#version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord;
#include "defines.h"
precision highp float;
in vec2 v_texcoord;
uniform sampler2D tex;
#if USE_BLUR
uniform vec2 uvSize;
uniform vec2 uvOffset;
uniform sampler2D blurredBG;
#endif
uniform float alpha;
#include "discard.glsl"
#include "tint.glsl"
#if USE_DISCARD
uniform bool discardOpaque;
uniform bool discardAlpha;
uniform float discardAlphaValue;
#endif
#if USE_TINT
uniform vec3 tint;
#endif
#if USE_ROUNDING
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl"
#include "surface_CM.glsl"
#endif
#if USE_CM
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
#if USE_TONEMAP || USE_SDR_MOD
uniform mat3 targetPrimariesXYZ;
#else
const mat3 targetPrimariesXYZ = mat3(0.0);
#endif
#include "CM.glsl"
#endif
layout(location = 0) out vec4 fragColor;
void main() {
#include "get_rgb_pixel.glsl"
#if USE_RGBA
vec4 pixColor = texture(tex, v_texcoord);
#else
vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);
#endif
#include "do_discard.glsl"
#include "do_CM.glsl"
#include "do_tint.glsl"
#include "do_rounding.glsl"
#if USE_DISCARD && !USE_BLUR
if (discardOpaque && pixColor.a * alpha == 1.0)
discard;
if (discardAlpha && pixColor.a <= discardAlphaValue)
discard;
#endif
#if USE_CM
pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
#if USE_ICC
,
iccLut3D, iccLutSize
#else
#if USE_TONEMAP || USE_SDR_MOD
,
targetPrimariesXYZ
#endif
#if USE_TONEMAP
,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
#endif
#if USE_SDR_MOD
,
sdrSaturation, sdrBrightnessMultiplier
#endif
#endif
);
#endif
#if USE_TINT
pixColor.rgb = pixColor.rgb * tint;
#endif
#if USE_ROUNDING
pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize);
#endif
#if USE_BLUR
#if USE_DISCARD
pixColor = mix(pixColor, vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0),
discardAlpha && (pixColor.a <= discardAlphaValue) ? 0.0 : 1.0);
#else
pixColor = vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0);
#endif
#endif
fragColor = pixColor * alpha;
}

View file

@ -1,4 +0,0 @@
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
#include "primaries_xyz.glsl"
#include "CM.glsl"

View file

@ -1 +0,0 @@
uniform vec3 tint;

View file

@ -1,17 +1,15 @@
uniform float maxLuminance;
uniform float dstMaxLuminance;
uniform float dstRefLuminance;
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#include "constants.h"
const mat3 BT2020toLMS = mat3(
0.3592, 0.6976, -0.0358,
-0.1922, 1.1004, 0.0755,
0.0070, 0.0749, 0.8434
);
const mat3 BT2020toLMS = mat3(0.3592, 0.6976, -0.0358, -0.1922, 1.1004, 0.0755, 0.0070, 0.0749, 0.8434);
//const mat3 LMStoBT2020 = inverse(BT2020toLMS);
const mat3 LMStoBT2020 = mat3(
2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081,
0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409,
-0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394
const mat3 LMStoBT2020 = mat3( //
2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, //
0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, //
-0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 //
);
// const mat3 ICtCpPQ = transpose(mat3(
@ -19,16 +17,16 @@ const mat3 LMStoBT2020 = mat3(
// 6610.0, -13613.0, 7003.0,
// 17933.0, -17390.0, -543.0
// ) / 4096.0);
const mat3 ICtCpPQ = mat3(
0.5, 1.61376953125, 4.378173828125,
0.5, -3.323486328125, -4.24560546875,
0.0, 1.709716796875, -0.132568359375
const mat3 ICtCpPQ = mat3( //
0.5, 1.61376953125, 4.378173828125, //
0.5, -3.323486328125, -4.24560546875, //
0.0, 1.709716796875, -0.132568359375 //
);
//const mat3 ICtCpPQInv = inverse(ICtCpPQ);
const mat3 ICtCpPQInv = mat3(
1.0, 1.0, 1.0,
0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118,
0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185
const mat3 ICtCpPQInv = mat3( //
1.0, 1.0, 1.0, //
0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, //
0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 //
);
// unused for now
@ -39,31 +37,28 @@ const mat3 ICtCpPQInv = mat3(
// ) / 4096.0);
// const mat3 ICtCpHLGInv = inverse(ICtCpHLG);
vec4 tonemap(vec4 color, mat3 dstXYZ) {
vec4 tonemap(vec4 color, mat3 dstXYZ, float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance) {
if (maxLuminance < dstMaxLuminance * 1.01)
return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]);
mat3 toLMS = BT2020toLMS * dstXYZ;
mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020;
mat3 toLMS = BT2020toLMS * dstXYZ;
mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020;
vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb;
vec3 ICtCp = ICtCpPQ * lms;
vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb;
vec3 ICtCp = ICtCpPQ * lms;
float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2);
float luminance = pow(
(max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E),
PQ_INV_M1
) * HDR_MAX_LUMINANCE;
float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2);
float luminance = pow((max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1) * HDR_MAX_LUMINANCE;
float linearPart = min(luminance, dstRefLuminance);
float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0);
float linearPart = min(luminance, dstRefLuminance);
float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0);
float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0);
float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0));
float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance);
float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance);
float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0));
float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance);
float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance);
// scale src to dst reference
float refScale = dstRefLuminance / srcRefLuminance;
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]);
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]);
}