瀏覽代碼

obs-transitions: Add HDR support to stinger

Regular SDR/HDR stingers, and SDR track matte should work. HDR track
matte might work, but would take a carefully crafted video that takes
the SDR white level into account, and this hasn't been tested.
jpark37 3 年之前
父節點
當前提交
6a871d3f66
共有 2 個文件被更改,包括 133 次插入13 次删除
  1. 24 3
      plugins/obs-transitions/data/stinger_matte_transition.effect
  2. 109 10
      plugins/obs-transitions/transition-stinger.c

+ 24 - 3
plugins/obs-transitions/data/stinger_matte_transition.effect

@@ -33,9 +33,9 @@ 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
+float4 StingerMatte(VertData f_in)
 {
-	float2 uv = v_in.uv;
+	float2 uv = f_in.uv;
 	float4 a_color = a_tex.Sample(textureSampler, uv);
 	float4 b_color = b_tex.Sample(textureSampler, uv);
 	float4 matte_color = matte_tex.Sample(textureSampler, uv);
@@ -51,15 +51,36 @@ float4 PSStingerMatte(VertData v_in) : TARGET
 	matte_luma = (invert_matte ? (1.0 - matte_luma) : matte_luma);
 
 	float4 rgba = lerp(a_color, b_color, matte_luma);
+	return rgba;
+}
+
+float4 PSStingerMatte(VertData f_in) : TARGET
+{
+	float4 rgba = StingerMatte(f_in);
 	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
 	return rgba;
 }
 
+float4 PSStingerMatteLinear(VertData f_in) : TARGET
+{
+	float4 rgba = StingerMatte(f_in);
+	return rgba;
+}
+
 technique StingerMatte
 {
 	pass
 	{
 		vertex_shader = VSDefault(v_in);
-		pixel_shader = PSStingerMatte(v_in);
+		pixel_shader = PSStingerMatte(f_in);
+	}
+}
+
+technique StingerMatteLinear
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader = PSStingerMatteLinear(f_in);
 	}
 }

+ 109 - 10
plugins/obs-transitions/transition-stinger.c

@@ -256,7 +256,16 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
 		float scale_x = (float)cx / matte_cx;
 		float scale_y = (float)cy / matte_cy;
 
-		if (gs_texrender_begin(s->matte_tex, cx, cy)) {
+		const enum gs_color_space space =
+			obs_source_get_color_space(matte_source, 0, NULL);
+		enum gs_color_format format = gs_get_format_from_space(space);
+		if (gs_texrender_get_format(s->matte_tex) != format) {
+			gs_texrender_destroy(s->matte_tex);
+			s->matte_tex = gs_texrender_create(format, GS_ZS_NONE);
+		}
+
+		if (gs_texrender_begin_with_color_space(s->matte_tex, cx, cy,
+							space)) {
 			gs_matrix_scale3f(scale_x, scale_y, 1.0f);
 			gs_matrix_translate3f(width_offset, height_offset,
 					      0.0f);
@@ -273,13 +282,23 @@ 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);
+	/* texture setters look reversed, but they aren't */
+	const char *tech_name = "StingerMatte";
+	if (gs_get_color_space() == GS_CS_SRGB) {
+		/* users want nonlinear fade */
+		gs_effect_set_texture(s->ep_a_tex, a);
+		gs_effect_set_texture(s->ep_b_tex, b);
+	} else {
+		/* nonlinear fade is too wrong, so use linear fade */
+		gs_effect_set_texture_srgb(s->ep_a_tex, a);
+		gs_effect_set_texture_srgb(s->ep_b_tex, b);
+		tech_name = "StingerMatteLinear";
+	}
 	gs_effect_set_texture(s->ep_matte_tex,
 			      gs_texrender_get_texture(s->matte_tex));
 	gs_effect_set_bool(s->ep_invert_matte, s->invert_matte);
 
-	while (gs_effect_loop(s->matte_effect, "StingerMatte"))
+	while (gs_effect_loop(s->matte_effect, tech_name))
 		gs_draw_sprite(NULL, 0, cx, cy);
 
 	gs_enable_framebuffer_srgb(previous);
@@ -289,9 +308,16 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
 
 static void stinger_texrender(struct stinger_info *s, uint32_t source_cx,
 			      uint32_t source_cy, uint32_t media_cx,
-			      uint32_t media_cy)
+			      uint32_t media_cy, enum gs_color_space space)
 {
-	if (gs_texrender_begin(s->stinger_tex, source_cx, source_cy)) {
+	enum gs_color_format format = gs_get_format_from_space(space);
+	if (gs_texrender_get_format(s->stinger_tex) != format) {
+		gs_texrender_destroy(s->stinger_tex);
+		s->stinger_tex = gs_texrender_create(format, GS_ZS_NONE);
+	}
+
+	if (gs_texrender_begin_with_color_space(s->stinger_tex, source_cx,
+						source_cy, space)) {
 		float cx = (float)media_cx / s->matte_width_factor;
 		float cy = (float)media_cy / s->matte_height_factor;
 
@@ -306,6 +332,50 @@ static void stinger_texrender(struct stinger_info *s, uint32_t source_cx,
 	}
 }
 
+static const char *
+get_tech_name_and_multiplier(enum gs_color_space current_space,
+			     enum gs_color_space source_space,
+			     float *multiplier)
+{
+	const char *tech_name = "Draw";
+	*multiplier = 1.f;
+
+	switch (source_space) {
+	case GS_CS_SRGB:
+	case GS_CS_SRGB_16F:
+		switch (current_space) {
+		case GS_CS_709_SCRGB:
+			tech_name = "DrawMultiply";
+			*multiplier = obs_get_video_sdr_white_level() / 80.0f;
+		}
+		break;
+	case GS_CS_709_EXTENDED:
+		switch (current_space) {
+		case GS_CS_SRGB:
+		case GS_CS_SRGB_16F:
+			tech_name = "DrawTonemap";
+			break;
+		case GS_CS_709_SCRGB:
+			tech_name = "DrawMultiply";
+			*multiplier = obs_get_video_sdr_white_level() / 80.0f;
+		}
+		break;
+	case GS_CS_709_SCRGB:
+		switch (current_space) {
+		case GS_CS_SRGB:
+		case GS_CS_SRGB_16F:
+			tech_name = "DrawMultiplyTonemap";
+			*multiplier = 80.0f / obs_get_video_sdr_white_level();
+			break;
+		case GS_CS_709_EXTENDED:
+			tech_name = "DrawMultiply";
+			*multiplier = 80.0f / obs_get_video_sdr_white_level();
+		}
+	}
+
+	return tech_name;
+}
+
 static void stinger_video_render(void *data, gs_effect_t *effect)
 {
 	struct stinger_info *s = data;
@@ -353,21 +423,38 @@ static void stinger_video_render(void *data, gs_effect_t *effect)
 		return;
 
 	if (s->do_texrender) {
-		stinger_texrender(s, source_cx, source_cy, media_cx, media_cy);
+		const enum gs_color_space space =
+			obs_source_get_color_space(s->media_source, 0, NULL);
+		stinger_texrender(s, source_cx, source_cy, media_cx, media_cy,
+				  space);
+
+		const bool previous = gs_framebuffer_srgb_enabled();
+		gs_enable_framebuffer_srgb(true);
+
+		float multiplier;
+		const char *technique = get_tech_name_and_multiplier(
+			gs_get_color_space(), space, &multiplier);
 
 		gs_effect_t *e = obs_get_base_effect(OBS_EFFECT_DEFAULT);
-		gs_eparam_t *p = gs_effect_get_param_by_name(e, "image");
+		gs_eparam_t *p_image = gs_effect_get_param_by_name(e, "image");
+		gs_eparam_t *p_multiplier =
+			gs_effect_get_param_by_name(e, "multiplier");
 		gs_texture_t *tex = gs_texrender_get_texture(s->stinger_tex);
 
-		gs_effect_set_texture(p, tex);
-		while (gs_effect_loop(e, "Draw"))
+		gs_effect_set_texture_srgb(p_image, tex);
+		gs_effect_set_float(p_multiplier, multiplier);
+		while (gs_effect_loop(e, technique))
 			gs_draw_sprite(NULL, 0, source_cx, source_cy);
+
+		gs_enable_framebuffer_srgb(previous);
 	} else {
+		const bool previous = gs_set_linear_srgb(true);
 		gs_matrix_push();
 		gs_matrix_scale3f(source_cxf / (float)media_cx,
 				  source_cyf / (float)media_cy, 1.0f);
 		obs_source_video_render(s->media_source);
 		gs_matrix_pop();
+		gs_set_linear_srgb(previous);
 	}
 
 	UNUSED_PARAMETER(effect);
@@ -792,6 +879,17 @@ static obs_missing_files_t *stinger_missing_files(void *data)
 	return files;
 }
 
+static enum gs_color_space
+stinger_get_color_space(void *data, size_t count,
+			const enum gs_color_space *preferred_spaces)
+{
+	UNUSED_PARAMETER(count);
+	UNUSED_PARAMETER(preferred_spaces);
+
+	struct stinger_info *s = data;
+	return obs_transition_video_get_color_space(s->source);
+}
+
 struct obs_source_info stinger_transition = {
 	.id = "obs_stinger_transition",
 	.type = OBS_SOURCE_TYPE_TRANSITION,
@@ -809,4 +907,5 @@ struct obs_source_info stinger_transition = {
 	.enum_all_sources = stinger_enum_all_sources,
 	.transition_start = stinger_transition_start,
 	.transition_stop = stinger_transition_stop,
+	.video_get_color_space = stinger_get_color_space,
 };