Преглед изворни кода

obs-transitions: Blend in linear space

Color mismatch is apparent when using source transitions, which lerps
against transparent black and blends into the canvas nonlinearly. When
the transition is done, the blend switches to linear, leading to a pop.

Fix the issue by blending into the canvas in linear space. The lerp is
still nonlinear by design.
jpark37 пре 4 година
родитељ
комит
9d7330ca44

+ 13 - 2
plugins/obs-transitions/data/fade_to_color_transition.effect

@@ -22,10 +22,21 @@ VertData VSDefault(VertData v_in)
 	return vert_out;
 }
 
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
 float4 PSFadeToColor(VertData v_in) : TARGET
 {
-	float4 premultiplied = float4(color.rgb * color.a, color.a);
-	return lerp(tex.Sample(textureSampler, v_in.uv), premultiplied, swp);
+	float4 rgba = lerp(tex.Sample(textureSampler, v_in.uv), color, swp);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
 }
 
 technique FadeToColor

+ 13 - 1
plugins/obs-transitions/data/fade_transition.effect

@@ -22,11 +22,23 @@ VertData VSDefault(VertData v_in)
 	return vert_out;
 }
 
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
 float4 PSFade(VertData v_in) : TARGET
 {
 	float4 a_val = tex_a.Sample(textureSampler, v_in.uv);
 	float4 b_val = tex_b.Sample(textureSampler, v_in.uv);
-	return lerp(a_val, b_val, fade_val);
+	float4 rgba = lerp(a_val, b_val, fade_val);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
 }
 
 technique Fade

+ 21 - 8
plugins/obs-transitions/data/luma_wipe_transition.effect

@@ -28,6 +28,16 @@ VertData VSDefault(VertData v_in)
 	return vert_out;
 }
 
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
 float4 PSLumaWipe(VertData v_in) : TARGET
 {
 	float2 uv = v_in.uv;
@@ -40,15 +50,18 @@ float4 PSLumaWipe(VertData v_in) : TARGET
 
 	float time = lerp(0.0f, 1.0f + softness, progress);
 
-	if (luma <= time - softness)
-		return b_color;
-
-	if (luma >= time)
-		return a_color;
-
-	float alpha = (time - luma) / softness;
+	float4 rgba;
+	if (luma <= time - softness) {
+		rgba = b_color;
+	} else if (luma >= time) {
+		rgba = a_color;
+	} else {
+		float alpha = (time - luma) / softness;
+		rgba = lerp(a_color, b_color, alpha);
+	}
 
-	return lerp(a_color, b_color, alpha);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
 }
 
 technique LumaWipe

+ 11 - 0
plugins/obs-transitions/data/slide_transition.effect

@@ -24,6 +24,16 @@ VertData VSDefault(VertData v_in)
 	return vert_out;
 }
 
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
 float4 PSSlide(VertData v_in) : TARGET
 {
 	float2 tex_a_uv = v_in.uv + tex_a_dir;
@@ -35,6 +45,7 @@ float4 PSSlide(VertData v_in) : TARGET
 		   ? tex_b.Sample(textureSampler, tex_b_uv)
 		   : tex_a.Sample(textureSampler, tex_a_uv);
 
+	outc.rgb = srgb_nonlinear_to_linear(outc.rgb);
 	return outc;
 }
 

+ 13 - 1
plugins/obs-transitions/data/stinger_matte_transition.effect

@@ -23,6 +23,16 @@ VertData VSDefault(VertData v_in)
 	return vert_out;
 }
 
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
 float4 PSStingerMatte(VertData v_in) : TARGET
 {
 	float2 uv = v_in.uv;
@@ -40,7 +50,9 @@ float4 PSStingerMatte(VertData v_in) : TARGET
 	// if matte invert is enabled, invert the matte color
 	matte_luma = (invert_matte ? (1.0 - matte_luma) : matte_luma);
 
-	return lerp(a_color, b_color, matte_luma);
+	float4 rgba = lerp(a_color, b_color, matte_luma);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
 }
 
 technique StingerMatte

+ 11 - 0
plugins/obs-transitions/data/swipe_transition.effect

@@ -22,6 +22,16 @@ VertData VSDefault(VertData v_in)
 	return vert_out;
 }
 
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
 float4 PSSwipe(VertData v_in) : TARGET
 {
 	float2 swipe_uv = v_in.uv + swipe_val;
@@ -32,6 +42,7 @@ float4 PSSwipe(VertData v_in) : TARGET
 		   ? tex_b.Sample(textureSampler, v_in.uv)
 		   : tex_a.Sample(textureSampler, swipe_uv);
 
+	outc.rgb = srgb_nonlinear_to_linear(outc.rgb);
 	return outc;
 }
 

+ 9 - 3
plugins/obs-transitions/transition-fade-to-color.c

@@ -105,13 +105,19 @@ static void fade_to_color_callback(void *data, gs_texture_t *a, gs_texture_t *b,
 
 	float swp = t < fade_to_color->switch_point ? sa : 1.0f - sb;
 
-	gs_effect_set_texture(fade_to_color->ep_tex,
-			      t < fade_to_color->switch_point ? a : b);
-	gs_effect_set_float(fade_to_color->ep_swp, swp);
+	gs_texture_t *const tex = (t < fade_to_color->switch_point) ? a : b;
+
+	const bool previous = gs_framebuffer_srgb_enabled();
+	gs_enable_framebuffer_srgb(true);
+
+	gs_effect_set_texture(fade_to_color->ep_tex, tex);
 	gs_effect_set_vec4(fade_to_color->ep_color, &fade_to_color->color);
+	gs_effect_set_float(fade_to_color->ep_swp, swp);
 
 	while (gs_effect_loop(fade_to_color->effect, "FadeToColor"))
 		gs_draw_sprite(NULL, 0, cx, cy);
+
+	gs_enable_framebuffer_srgb(previous);
 }
 
 static void fade_to_color_video_render(void *data, gs_effect_t *effect)

+ 5 - 0
plugins/obs-transitions/transition-fade.c

@@ -53,12 +53,17 @@ static void fade_callback(void *data, gs_texture_t *a, gs_texture_t *b, float t,
 {
 	struct fade_info *fade = data;
 
+	const bool previous = gs_framebuffer_srgb_enabled();
+	gs_enable_framebuffer_srgb(true);
+
 	gs_effect_set_texture(fade->a_param, a);
 	gs_effect_set_texture(fade->b_param, b);
 	gs_effect_set_float(fade->fade_param, t);
 
 	while (gs_effect_loop(fade->effect, "Fade"))
 		gs_draw_sprite(NULL, 0, cx, cy);
+
+	gs_enable_framebuffer_srgb(previous);
 }
 
 static void fade_video_render(void *data, gs_effect_t *effect)

+ 5 - 0
plugins/obs-transitions/transition-luma-wipe.c

@@ -165,6 +165,9 @@ static void luma_wipe_callback(void *data, gs_texture_t *a, gs_texture_t *b,
 {
 	struct luma_wipe_info *lwipe = data;
 
+	const bool previous = gs_framebuffer_srgb_enabled();
+	gs_enable_framebuffer_srgb(true);
+
 	gs_effect_set_texture(lwipe->ep_a_tex, a);
 	gs_effect_set_texture(lwipe->ep_b_tex, b);
 	gs_effect_set_texture(lwipe->ep_l_tex, lwipe->luma_image.texture);
@@ -175,6 +178,8 @@ static void luma_wipe_callback(void *data, gs_texture_t *a, gs_texture_t *b,
 
 	while (gs_effect_loop(lwipe->effect, "LumaWipe"))
 		gs_draw_sprite(NULL, 0, cx, cy);
+
+	gs_enable_framebuffer_srgb(previous);
 }
 
 void luma_wipe_video_render(void *data, gs_effect_t *effect)

+ 5 - 0
plugins/obs-transitions/transition-slide.c

@@ -93,6 +93,9 @@ static void slide_callback(void *data, gs_texture_t *a, gs_texture_t *b,
 	vec2_mulf(&tex_a_dir, &tex_a_dir, t);
 	vec2_mulf(&tex_b_dir, &tex_b_dir, 1.0f - t);
 
+	const bool previous = gs_framebuffer_srgb_enabled();
+	gs_enable_framebuffer_srgb(true);
+
 	gs_effect_set_texture(slide->a_param, a);
 	gs_effect_set_texture(slide->b_param, b);
 
@@ -101,6 +104,8 @@ static void slide_callback(void *data, gs_texture_t *a, gs_texture_t *b,
 
 	while (gs_effect_loop(slide->effect, "Slide"))
 		gs_draw_sprite(NULL, 0, cx, cy);
+
+	gs_enable_framebuffer_srgb(previous);
 }
 
 void slide_video_render(void *data, gs_effect_t *effect)

+ 5 - 0
plugins/obs-transitions/transition-stinger.c

@@ -236,6 +236,9 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
 		}
 	}
 
+	const bool previous = gs_framebuffer_srgb_enabled();
+	gs_enable_framebuffer_srgb(true);
+
 	gs_effect_set_texture(s->ep_a_tex, a);
 	gs_effect_set_texture(s->ep_b_tex, b);
 	gs_effect_set_texture(s->ep_matte_tex,
@@ -245,6 +248,8 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
 	while (gs_effect_loop(s->matte_effect, "StingerMatte"))
 		gs_draw_sprite(NULL, 0, cx, cy);
 
+	gs_enable_framebuffer_srgb(previous);
+
 	UNUSED_PARAMETER(t);
 }
 

+ 5 - 0
plugins/obs-transitions/transition-swipe.c

@@ -88,12 +88,17 @@ static void swipe_callback(void *data, gs_texture_t *a, gs_texture_t *b,
 
 	vec2_mulf(&swipe_val, &swipe_val, swipe->swipe_in ? 1.0f - t : t);
 
+	const bool previous = gs_framebuffer_srgb_enabled();
+	gs_enable_framebuffer_srgb(true);
+
 	gs_effect_set_texture(swipe->a_param, swipe->swipe_in ? b : a);
 	gs_effect_set_texture(swipe->b_param, swipe->swipe_in ? a : b);
 	gs_effect_set_vec2(swipe->swipe_param, &swipe_val);
 
 	while (gs_effect_loop(swipe->effect, "Swipe"))
 		gs_draw_sprite(NULL, 0, cx, cy);
+
+	gs_enable_framebuffer_srgb(previous);
 }
 
 static void swipe_video_render(void *data, gs_effect_t *effect)