From 73f925763384a48c8f3bad085d809d4ced2884b6 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Tue, 2 Feb 2021 22:22:47 +0100 Subject: [PATCH] effects: Add RGB, HSV, and YUV conversion functions --- CMakeLists.txt | 4 +- data/effects/color-conversion.effect | 81 -------------------- data/effects/color_conversion_rgb_hsl.effect | 56 ++++++++++++++ data/effects/color_conversion_rgb_hsv.effect | 32 ++++++++ data/effects/color_conversion_rgb_yuv.effect | 19 +++++ data/effects/shared.effect | 4 +- 6 files changed, 113 insertions(+), 83 deletions(-) delete mode 100644 data/effects/color-conversion.effect create mode 100644 data/effects/color_conversion_rgb_hsl.effect create mode 100644 data/effects/color_conversion_rgb_hsv.effect create mode 100644 data/effects/color_conversion_rgb_yuv.effect diff --git a/CMakeLists.txt b/CMakeLists.txt index 01935972..ee4c28f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -967,7 +967,9 @@ list(APPEND PROJECT_PRIVATE_SOURCE "source/obs/obs-tools.cpp" ) list(APPEND PROJECT_DATA - "data/effects/color-conversion.effect" + "data/effects/color_conversion_rgb_hsl.effect" + "data/effects/color_conversion_rgb_hsv.effect" + "data/effects/color_conversion_rgb_yuv.effect" "data/effects/mipgen.effect" "data/effects/pack-unpack.effect" "data/effects/shared.effect" diff --git a/data/effects/color-conversion.effect b/data/effects/color-conversion.effect deleted file mode 100644 index 30a3f1e9..00000000 --- a/data/effects/color-conversion.effect +++ /dev/null @@ -1,81 +0,0 @@ -uniform float4x4 ViewProj; -uniform texture2d image; - -sampler_state primarySampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertDataOut VSDefault(VertDataIn v_in) { - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv; - return vert_out; -} - -// ************************************************************************** // -// RGB <-> YUV -// ************************************************************************** // -float4 RGBtoYUV(float4 rgba, float3x3 yuv) { - return float4( - rgba.r * yuv._m00 + rgba.g * yuv._m01 + rgba.b * yuv._m02, - rgba.r * yuv._m10 + rgba.g * yuv._m11 + rgba.b * yuv._m12, - rgba.r * yuv._m20 + rgba.g * yuv._m21 + rgba.b * yuv._m22, - rgba.a - ) + float4(0,0.5,0.5,0); -} -float4 YUVtoRGB(float4 yuva, float3x3 yuvi) { - yuva.gb -= 0.5; - return float4( - yuva.r * yuvi._m00 + yuva.g * yuvi._m01 + yuva.b * yuvi._m02, - yuva.r * yuvi._m10 + yuva.g * yuvi._m11 + yuva.b * yuvi._m12, - yuva.r * yuvi._m20 + yuva.g * yuvi._m21 + yuva.b * yuvi._m22, - yuva.a); -} - -float4 RGBToYUV_Pass(VertDataOut v_in) : TARGET { - const float3x3 mYUV709 = { - 0.2126, 0.7152, 0.0722, - -0.2126, -0.7152, 0.9278, - 0.7874, -0.7152, -0.0722 - }; - const float3x3 mYUV709n = { // Normalized - 0.2126, 0.7152, 0.0722, - -0.1145721060573399, -0.3854278939426601, 0.5, - 0.5, -0.4541529083058166, -0.0458470916941834 - }; - return RGBtoYUV(image.Sample(primarySampler, v_in.uv), mYUV709n); -} - -float4 YUVToRGB_Pass(VertDataOut v_in) : TARGET { - const float3x3 mYUV709i = { // Inverse Normalized - 1, 0, 1.5748, - 1, -0.187324, -0.468124, - 1, 1.8556, 0 - }; - return YUVtoRGB(image.Sample(primarySampler, v_in.uv), mYUV709i); -} - -technique RGBToYUV { - pass { - vertex_shader = VSDefault(v_in); - pixel_shader = RGBToYUV_Pass(v_in); - } -} -technique YUVToRGB { - pass { - vertex_shader = VSDefault(v_in); - pixel_shader = YUVToRGB_Pass(v_in); - } -} diff --git a/data/effects/color_conversion_rgb_hsl.effect b/data/effects/color_conversion_rgb_hsl.effect new file mode 100644 index 00000000..eb391d6e --- /dev/null +++ b/data/effects/color_conversion_rgb_hsl.effect @@ -0,0 +1,56 @@ +// This may have odd/incorrect results. + +float3 RGBtoHSL(float3 rgb) { + float h = 0.0; + float s = 0.0; + float l = 0.0; + float r = rgb.r; + float g = rgb.g; + float b = rgb.b; + float cMin = min( r, min( g, b ) ); + float cMax = max( r, max( g, b ) ); + + l = ( cMax + cMin ) / 2.0; + if ( cMax > cMin ) { + float cDelta = cMax - cMin; + + //s = l < .05 ? cDelta / ( cMax + cMin ) : cDelta / ( 2.0 - ( cMax + cMin ) ); Original + s = l < .0 ? cDelta / ( cMax + cMin ) : cDelta / ( 2.0 - ( cMax + cMin ) ); + + if ( r == cMax ) { + h = ( g - b ) / cDelta; + } else if ( g == cMax ) { + h = 2.0 + ( b - r ) / cDelta; + } else { + h = 4.0 + ( r - g ) / cDelta; + } + + if ( h < 0.0) { + h += 6.0; + } + h = h / 6.0; + } + return float3( h, s, l ); +} + +float4 RGBAtoHSLA(float4 rgba) { + return float4(RGBtoHSL(rgba.rgb), rgba.a); +} + +float3 HSLtoRGB(float3 hsl) { + float3 rgb = clamp( + abs( + fmod( + hsl.x * 6.0 + float3(0.0, 4.0, 2.0), + 6.0 + ) - 3.0 + ) - 1.0, + 0.0, + 1.0 + ); + return hsl.z + hsl.y * (rgb - 0.5) * (1.0 - abs(2.0 * hsl.z - 1.0)); +}; + +float4 HSLAtoRGBA(float4 hsla) { + return float4(HSLtoRGB(hsla.rgb), hsla.a); +} diff --git a/data/effects/color_conversion_rgb_hsv.effect b/data/effects/color_conversion_rgb_hsv.effect new file mode 100644 index 00000000..fbd85e7d --- /dev/null +++ b/data/effects/color_conversion_rgb_hsv.effect @@ -0,0 +1,32 @@ +// Adapted from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl + +//#define RGB_HSV_FASTCONDITIONALMOVE + +float3 RGBtoHSV(float3 rgb) { + const float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + const float e = 1.0e-10; +#ifdef RGB_HSV_FASTCONDITIONALMOVE + float4 p = rgb.g < rgb.b ? float4(rgb.bg, K.wz) : float4(rgb.gb, K.xy); + float4 q = rgb.r < p.x ? float4(p.xyw, rgb.r) : float4(rgb.r, p.yzx); +#else + float4 p = lerp(float4(rgb.bg, K.wz), float4(rgb.gb, K.xy), step(rgb.b, rgb.g)); + float4 q = lerp(float4(p.xyw, rgb.r), float4(rgb.r, p.yzx), step(p.x, rgb.r)); +#endif + float d = q.x - min(q.w, q.y); + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +float4 RGBAtoHSVA(float4 rgba) { + return float4(RGBtoHSV(rgba.rgb), rgba.a); +} + +float3 HSVtoRGB(float3 hsv) { + const float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 v = float3(0,0,0); + v.rgb = hsv.z * lerp(K.xxx, clamp(abs(frac(hsv.xxx + K.xyz) * 6.0 - K.www) - K.xxx, 0.0, 1.0), hsv.y); + return v; +} + +float4 HSVAtoRGBA(float4 hsva) { + return float4(HSVtoRGB(hsv.rgb), hsva.a); +} diff --git a/data/effects/color_conversion_rgb_yuv.effect b/data/effects/color_conversion_rgb_yuv.effect new file mode 100644 index 00000000..a9626af6 --- /dev/null +++ b/data/effects/color_conversion_rgb_yuv.effect @@ -0,0 +1,19 @@ +#define YUV_709_ float3x3(0.2126, 0.7152, 0.0722, -0.2126, -0.7152, 0.9278, 0.7874, -0.7152, -0.0722) +#define YUV_709_NORM float3x3(0.2126, 0.7152, 0.0722, -0.1145721060573399, -0.3854278939426601, 0.5, 0.5, -0.4541529083058166, -0.0458470916941834) +#define YUV_709_INVNORM float3x3(1, 0, 1.5748, 1, -0.187324, -0.468124, 1, 1.8556, 0) + +float3 RGBtoYUV(float3 rgb, float3x3 m) { + return mul(m, rgb) + float3(0, .5, .5); +} + +float4 RGBAtoYUVA(float4 rgba, float3x3 m) { + return float4(RGBtoYUV(rgba.rgb, m), rgba.a); +} + +float3 YUVtoRGB(float3 yuv, float3x3 m) { + return mul(m, yuv - float3(0, .5, .5)); +} + +float4 YUVAtoRGBA(float4 yuva, float3x3 m) { + return float4(YUVtoRGB(yuva.rgb, m), yuva.a); +} diff --git a/data/effects/shared.effect b/data/effects/shared.effect index 4a1f57c6..56d4c216 100644 --- a/data/effects/shared.effect +++ b/data/effects/shared.effect @@ -51,4 +51,6 @@ VertexData DefaultVertexShader(VertexData vtx) { //------------------------------------------------------------------------------ // Color Conversion //------------------------------------------------------------------------------ - +#include "color_conversion_rgb_yuv.effect" +#include "color_conversion_rgb_hsv.effect" +#include "color_conversion_rgb_hsl.effect"