diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 5ef0c92b2..5f13ec205 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -27,6 +27,7 @@ set(obs-filters_SOURCES scroll-filter.c chroma-key-filter.c color-key-filter.c + color-grade-filter.c sharpness-filter.c gain-filter.c noise-gate-filter.c diff --git a/plugins/obs-filters/color-grade-filter.c b/plugins/obs-filters/color-grade-filter.c new file mode 100644 index 000000000..8adfdbf4f --- /dev/null +++ b/plugins/obs-filters/color-grade-filter.c @@ -0,0 +1,159 @@ +#include +#include +#include + +#define SETTING_IMAGE_PATH "image_path" +#define SETTING_CLUT_AMOUNT "clut_amount" + +#define TEXT_IMAGE_PATH obs_module_text("Path") +#define TEXT_AMOUNT obs_module_text("Amount") + +struct lut_filter_data { + obs_source_t *context; + gs_effect_t *effect; + gs_texture_t *target; + gs_image_file_t image; + + char *file; + float clut_amount; +}; + +static const char *color_grade_filter_get_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("ColorGradeFilter"); +} + +static void color_grade_filter_update(void *data, obs_data_t *settings) +{ + struct lut_filter_data *filter = data; + + const char *path = obs_data_get_string(settings, SETTING_IMAGE_PATH); + double clut_amount = obs_data_get_double(settings, SETTING_CLUT_AMOUNT); + + bfree(filter->file); + if (path) + filter->file = bstrdup(path); + + obs_enter_graphics(); + gs_image_file_free(&filter->image); + obs_leave_graphics(); + + gs_image_file_init(&filter->image, path); + + obs_enter_graphics(); + + gs_image_file_init_texture(&filter->image); + + filter->target = filter->image.texture; + filter->clut_amount = (float)clut_amount; + + char *effect_path = obs_module_file("color_grade_filter.effect"); + gs_effect_destroy(filter->effect); + filter->effect = gs_effect_create_from_file(effect_path, NULL); + bfree(effect_path); + + obs_leave_graphics(); +} + +static void color_grade_filter_defaults(obs_data_t *settings) +{ + obs_data_set_default_double(settings, SETTING_CLUT_AMOUNT, 1); +} + +static obs_properties_t *color_grade_filter_properties(void *data) +{ + struct lut_filter_data *s = data; + struct dstr path = {0}; + const char *slash; + + obs_properties_t *props = obs_properties_create(); + struct dstr filter_str = {0}; + + dstr_cat(&filter_str, "(*.png)"); + + if (s && s->file && *s->file) { + dstr_copy(&path, s->file); + } else { + dstr_copy(&path, obs_module_file("LUTs")); + dstr_cat_ch(&path, '/'); + } + + dstr_replace(&path, "\\", "/"); + slash = strrchr(path.array, '/'); + if (slash) + dstr_resize(&path, slash - path.array + 1); + + obs_properties_add_path(props, SETTING_IMAGE_PATH, TEXT_IMAGE_PATH, + OBS_PATH_FILE, filter_str.array, path.array); + obs_properties_add_float_slider(props, SETTING_CLUT_AMOUNT, + TEXT_AMOUNT, 0, 1, 0.01); + + dstr_free(&filter_str); + + UNUSED_PARAMETER(data); + return props; +} + +static void *color_grade_filter_create( + obs_data_t *settings, obs_source_t *context) +{ + struct lut_filter_data *filter = + bzalloc(sizeof(struct lut_filter_data)); + filter->context = context; + + obs_source_update(context, settings); + return filter; +} + +static void color_grade_filter_destroy(void *data) +{ + struct lut_filter_data *filter = data; + + obs_enter_graphics(); + gs_effect_destroy(filter->effect); + gs_image_file_free(&filter->image); + obs_leave_graphics(); + + bfree(filter->file); + bfree(filter); +} + +static void color_grade_filter_render(void *data, gs_effect_t *effect) +{ + struct lut_filter_data *filter = data; + obs_source_t *target = obs_filter_get_target(filter->context); + gs_eparam_t *param; + + if (!target || !filter->target || !filter->effect) { + obs_source_skip_video_filter(filter->context); + return; + } + + if (!obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_ALLOW_DIRECT_RENDERING)) + return; + + param = gs_effect_get_param_by_name(filter->effect, "clut"); + gs_effect_set_texture(param, filter->target); + + param = gs_effect_get_param_by_name(filter->effect, "clut_amount"); + gs_effect_set_float(param, filter->clut_amount); + + obs_source_process_filter_end(filter->context, filter->effect, 0, 0); + + UNUSED_PARAMETER(effect); +} + +struct obs_source_info color_grade_filter = { + .id = "clut_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = color_grade_filter_get_name, + .create = color_grade_filter_create, + .destroy = color_grade_filter_destroy, + .update = color_grade_filter_update, + .get_defaults = color_grade_filter_defaults, + .get_properties = color_grade_filter_properties, + .video_render = color_grade_filter_render +}; diff --git a/plugins/obs-filters/data/LUTs/black_and_white.png b/plugins/obs-filters/data/LUTs/black_and_white.png new file mode 100644 index 000000000..d55914c02 Binary files /dev/null and b/plugins/obs-filters/data/LUTs/black_and_white.png differ diff --git a/plugins/obs-filters/data/LUTs/original.png b/plugins/obs-filters/data/LUTs/original.png new file mode 100644 index 000000000..ed814dfbb Binary files /dev/null and b/plugins/obs-filters/data/LUTs/original.png differ diff --git a/plugins/obs-filters/data/LUTs/posterize.png b/plugins/obs-filters/data/LUTs/posterize.png new file mode 100644 index 000000000..2ef1e1deb Binary files /dev/null and b/plugins/obs-filters/data/LUTs/posterize.png differ diff --git a/plugins/obs-filters/data/LUTs/red_isolated.png b/plugins/obs-filters/data/LUTs/red_isolated.png new file mode 100644 index 000000000..624dc9791 Binary files /dev/null and b/plugins/obs-filters/data/LUTs/red_isolated.png differ diff --git a/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png b/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png new file mode 100644 index 000000000..abc162cc2 Binary files /dev/null and b/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png differ diff --git a/plugins/obs-filters/data/color_grade_filter.effect b/plugins/obs-filters/data/color_grade_filter.effect new file mode 100644 index 000000000..bbc8b5ed6 --- /dev/null +++ b/plugins/obs-filters/data/color_grade_filter.effect @@ -0,0 +1,66 @@ +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform texture2d clut; +uniform float clut_amount; + +sampler_state textureSampler { + 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.uv = v_in.uv; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + return vert_out; +} + +float4 LUT(VertDataOut v_in) : TARGET +{ + float4 textureColor = image.Sample(textureSampler, v_in.uv); + float blueColor = textureColor.b * 63.0; + + float2 quad1; + quad1.y = floor(floor(blueColor) / 8.0); + quad1.x = floor(blueColor) - (quad1.y * 8.0); + + float2 quad2; + quad2.y = floor(ceil(blueColor) / 8.0); + quad2.x = ceil(blueColor) - (quad2.y * 8.0); + + float2 texPos1; + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); + texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + + float2 texPos2; + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); + texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + + float4 newColor1 = clut.Sample(textureSampler, texPos1); + float4 newColor2 = clut.Sample(textureSampler, texPos2); + float4 luttedColor = lerp(newColor1, newColor2, frac(blueColor)); + + return lerp(textureColor, luttedColor, clut_amount); +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = LUT(v_in); + } +} diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index 232c24d19..eda64bc43 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -1,4 +1,5 @@ ColorFilter="Color Correction" +ColorGradeFilter="Color Grade" MaskFilter="Image Mask/Blend" AsyncDelayFilter="Video Delay (Async)" CropFilter="Crop/Pad" @@ -64,3 +65,4 @@ ScaleFiltering.Lanczos="Lanczos" NoiseSuppress.SuppressLevel="Suppression Level (dB)" Saturation="Saturation" HueShift="Hue Shift" +Amount="Amount" \ No newline at end of file diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index fbcdb91ed..b7234ff08 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -12,6 +12,7 @@ extern struct obs_source_info color_filter; extern struct obs_source_info scale_filter; extern struct obs_source_info scroll_filter; extern struct obs_source_info color_key_filter; +extern struct obs_source_info color_grade_filter; extern struct obs_source_info sharpness_filter; extern struct obs_source_info chroma_key_filter; extern struct obs_source_info async_delay_filter; @@ -29,6 +30,7 @@ bool obs_module_load(void) obs_register_source(&scale_filter); obs_register_source(&scroll_filter); obs_register_source(&color_key_filter); + obs_register_source(&color_grade_filter); obs_register_source(&sharpness_filter); obs_register_source(&chroma_key_filter); obs_register_source(&async_delay_filter);