Просмотр исходного кода

Merge pull request #4587 from jpark37/alpha-hack

Apply alpha in nonlinear space for images and async video
Jim 4 лет назад
Родитель
Сommit
81e9ad818f
3 измененных файлов с 84 добавлено и 15 удалено
  1. 38 0
      libobs/data/default.effect
  2. 25 6
      libobs/obs-source.c
  3. 21 9
      plugins/image-source/image-source.c

+ 38 - 0
libobs/data/default.effect

@@ -33,6 +33,35 @@ float4 PSDrawAlphaDivide(VertInOut vert_in) : TARGET
 	return float4(rgba.rgb * multiplier, alpha);
 }
 
+float srgb_linear_to_nonlinear_channel(float u)
+{
+	return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1.0 / 2.4)) - 0.055);
+}
+
+float3 srgb_linear_to_nonlinear(float3 v)
+{
+	return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));
+}
+
+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 PSDrawNonlinearAlpha(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
+	rgba.rgb *= rgba.a;
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
+}
+
 technique Draw
 {
 	pass
@@ -50,3 +79,12 @@ technique DrawAlphaDivide
 		pixel_shader  = PSDrawAlphaDivide(vert_in);
 	}
 }
+
+technique DrawNonlinearAlpha
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawNonlinearAlpha(vert_in);
+	}
+}

+ 25 - 6
libobs/obs-source.c

@@ -2056,17 +2056,30 @@ static void obs_source_draw_async_texture(struct obs_source *source)
 {
 	gs_effect_t *effect = gs_get_effect();
 	bool def_draw = (!effect);
+	bool premultiplied = false;
 	gs_technique_t *tech = NULL;
 
 	if (def_draw) {
 		effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
-		tech = gs_effect_get_technique(effect, "Draw");
+		const bool linear = gs_get_linear_srgb();
+		const char *tech_name = linear ? "DrawNonlinearAlpha" : "Draw";
+		premultiplied = linear;
+		tech = gs_effect_get_technique(effect, tech_name);
 		gs_technique_begin(tech);
 		gs_technique_begin_pass(tech, 0);
 	}
 
+	if (premultiplied) {
+		gs_blend_state_push();
+		gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
+	}
+
 	obs_source_draw_texture(source, effect);
 
+	if (premultiplied) {
+		gs_blend_state_pop();
+	}
+
 	if (def_draw) {
 		gs_technique_end_pass(tech);
 		gs_technique_end(tech);
@@ -3701,10 +3714,11 @@ bool obs_source_process_filter_begin(obs_source_t *filter,
 		filter->filter_texrender =
 			gs_texrender_create(format, GS_ZS_NONE);
 
-	gs_blend_state_push();
-	gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
-
 	if (gs_texrender_begin(filter->filter_texrender, cx, cy)) {
+		gs_blend_state_push();
+		gs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_ZERO,
+					   GS_BLEND_ONE, GS_BLEND_ZERO);
+
 		bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
 		bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
 		struct vec4 clear_color;
@@ -3718,10 +3732,10 @@ bool obs_source_process_filter_begin(obs_source_t *filter,
 		else
 			obs_source_video_render(target);
 
+		gs_blend_state_pop();
+
 		gs_texrender_end(filter->filter_texrender);
 	}
-
-	gs_blend_state_pop();
 	return true;
 }
 
@@ -3748,6 +3762,9 @@ static void obs_source_process_filter_tech_end_internal(
 
 	const char *tech = tech_name ? tech_name : "Draw";
 
+	gs_blend_state_push();
+	gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
+
 	if (can_bypass(target, parent, parent_flags, filter->allow_direct)) {
 		render_filter_bypass(target, effect, tech);
 	} else {
@@ -3757,6 +3774,8 @@ static void obs_source_process_filter_tech_end_internal(
 		}
 	}
 
+	gs_blend_state_pop();
+
 	gs_set_linear_srgb(previous);
 }
 

+ 21 - 9
plugins/image-source/image-source.c

@@ -147,19 +147,31 @@ static void image_source_render(void *data, gs_effect_t *effect)
 	if (!context->if2.image.texture)
 		return;
 
-	const bool linear_srgb = gs_get_linear_srgb();
+	effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
+	gs_technique_t *tech =
+		gs_effect_get_technique(effect, "DrawNonlinearAlpha");
 
 	const bool previous = gs_framebuffer_srgb_enabled();
-	gs_enable_framebuffer_srgb(linear_srgb);
+	gs_enable_framebuffer_srgb(true);
+
+	gs_blend_state_push();
+	gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
 
 	gs_eparam_t *const param = gs_effect_get_param_by_name(effect, "image");
-	if (linear_srgb)
-		gs_effect_set_texture_srgb(param, context->if2.image.texture);
-	else
-		gs_effect_set_texture(param, context->if2.image.texture);
+	gs_effect_set_texture_srgb(param, context->if2.image.texture);
+
+	size_t passes = gs_technique_begin(tech);
+	for (size_t i = 0; i < passes; i++) {
+		gs_technique_begin_pass(tech, i);
+
+		gs_draw_sprite(context->if2.image.texture, 0,
+			       context->if2.image.cx, context->if2.image.cy);
+
+		gs_technique_end_pass(tech);
+	}
+	gs_technique_end(tech);
 
-	gs_draw_sprite(context->if2.image.texture, 0, context->if2.image.cx,
-		       context->if2.image.cy);
+	gs_blend_state_pop();
 
 	gs_enable_framebuffer_srgb(previous);
 }
@@ -298,7 +310,7 @@ static obs_missing_files_t *image_source_missingfiles(void *data)
 static struct obs_source_info image_source_info = {
 	.id = "image_source",
 	.type = OBS_SOURCE_TYPE_INPUT,
-	.output_flags = OBS_SOURCE_VIDEO,
+	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
 	.get_name = image_source_get_name,
 	.create = image_source_create,
 	.destroy = image_source_destroy,