当前位置: 首页 > 工具软件 > Chroma > 使用案例 >

OBS:绿幕抠像 chroma_key_filter.effect 笔记

商开宇
2023-12-01

近期对obs studio源码进行解析,对obs_filter 的绿幕抠图进行理解,期间看源码文件看了很多相关的,一直没有发现绿幕抠像的算法,最后在chroma_key_filter.effect文件中找到,发现网上effect文件的讲解很少,因此做个笔记。

//uniform 可以在各个程序间共享
uniform float4x4 ViewProj;//表示世界矩阵
uniform texture2d image;//纹理变量

//uniform float4x4 yuv_mat = { 0.182586,  0.614231,  0.062007, 0.062745,
//                            -0.100644, -0.338572,  0.439216, 0.501961,
//                             0.439216, -0.398942, -0.040274, 0.501961,
//                             0.000000,  0.000000,  0.000000, 1.000000};
//uniform float4x4 yuv_mat = { 0.216,  0.7152,  0.0722, 0.062745,
//                            -0.09991, -0.33609,  0.436, 0.501961,
//                             0.615, -0.55861, -0.05639, 0.501961,
//                             0.000000,  0.000000,  0.000000, 1.000000};//BT.709标准
//uniform float4x4 yuv_mat = { 0.299,  0.587,  0.114, 0.062745,
//                             -0.14713, -0.28886,  0.436, 0.501961,
//                             0.615, -0.51499, -0.10001, 0.501961,
//                             0.000000,  0.000000,  0.000000, 1.000000};//BT.601标准
uniform float4x4 yuv_mat = { 0.29882,  0.58681,  0.114363, 0.062745,
                             -0.172485, -0.338718,  0.511207, 0.501961,
                             0.51155, -0.42811, -0.08343, 0.501961,
                             0.000000,  0.000000,  0.000000, 1.000000};//常规标准
uniform float4 color;
uniform float contrast;
uniform float brightness;
uniform float gamma;

uniform float2 chroma_key;
uniform float2 pixel_size;
uniform float similarity;
uniform float smoothness;
uniform float spill;

//纹理采集器
sampler_state textureSampler {
	Filter    = Linear;//使用线性滤波
	AddressU  = Clamp;//U V方向上的纹理寻址模式都采用clamp方式
	AddressV  = Clamp;
};
/*
结构体包含存储的数据(vertex shader 向 pixel_shader 传递的数据值)
":"后面POSITION告诉GPU在哪个寄存器放置这个值(寄存器是GPU中保存数据的一个容器)
过滤是指通过给定的uv坐标从纹理贴图中获取图素的一种方法
mipmap由一系列纹理组成,其中每张纹理是一张分辨率逐渐降低的图像,每一级的高度和宽度都是由
前一级的高度和宽度的一半,使用mipmap可以确保无论是靠近还是远离纹理,纹理都可以保持它原有的真实性和质量,从而减少了方法和缩小所带来的负面效果
*/
struct VertData {
	float4 pos : POSITION;
	float2 uv  : TEXCOORD0;
};
/*
当定义一个shader传递到pixel shader的变量时,必须决定在GPU的何处保存这个值;
在这里创建一个vert_out的结构体,此结构必须被填充并从函数返回,以便后继过程处理;
输入参数不参与后继过程处理,但需要乘以ViewProj矩阵使之以正确放置在屏幕上。
*/
VertData VSDefault(VertData v_in)
{
	VertData vert_out;
	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
	vert_out.uv  = v_in.uv;
	return vert_out;//返回实例来决定传递什么数据
}

float4 CalcColor(float4 rgba)
{
	return float4(pow(rgba.rgb, float3(gamma, gamma, gamma)) * contrast + brightness, rgba.a);
}

float GetChromaDist(float3 rgb)
{
	float4 yuvx = mul(float4(rgb.rgb, 1.0), yuv_mat);
	return distance(chroma_key, yuvx.yz);//rgb 在转成yuvx后,与设置的颜色计算距离,即相似度,越接近距离越小
}

float4 SampleTexture(float2 uv)
{
	return image.Sample(textureSampler, uv);
}

float GetBoxFilteredChromaDist(float3 rgb, float2 texCoord)
{
	float2 h_pixel_size = pixel_size / 2.0;
	float2 point_0 = float2(pixel_size.x, h_pixel_size.y);
	float2 point_1 = float2(h_pixel_size.x, -pixel_size.y);
	float distVal = GetChromaDist(SampleTexture(texCoord-point_0).rgb);
	distVal += GetChromaDist(SampleTexture(texCoord+point_0).rgb);
	distVal += GetChromaDist(SampleTexture(texCoord-point_1).rgb);
	distVal += GetChromaDist(SampleTexture(texCoord+point_1).rgb);
	distVal *= 2.0;
	distVal += GetChromaDist(rgb);
	//float distVal = GetChromaDist(rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord - pixel_size).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord - float2(pixel_size.x,0.0)).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord - float2(pixel_size.x,-pixel_size.y)).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord - float2(0.0,pixel_size.y)).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord + float2(0.0,pixel_size.y)).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord + float2(pixel_size.x,-pixel_size.y)).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord + float2(pixel_size.x,0.0)).rgb);
	//distVal += GetChromaDist(SampleTexture(texCoord + pixel_size).rgb);
	
	
	return distVal / 9.0; //计算9个点的距离均值
}

float4 ProcessChromaKey(float4 rgba, VertData v_in)
{
	float chromaDist = GetBoxFilteredChromaDist(rgba.rgb, v_in.uv);
	float baseMask = chromaDist - similarity;//相似度 计算差值
	float fullMask = pow(saturate(baseMask / smoothness), 1.5);
	float spillVal = pow(saturate(baseMask / spill), 1.5);

	rgba.rgba *= color;
	rgba.a *= fullMask;//绿色时alpah 是透明的,非绿色时为不透明
	//计算灰度图
	float desat = (rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722);
	//saturate(x)如果 x 小于 0,返回 0;如果 x 大于 1,返回1;否则,返回 x
	rgba.rgb = saturate(float3(desat, desat, desat)) * (1.0 - spillVal) + rgba.rgb * spillVal; //抠图
	
	
	return CalcColor(rgba);
}

float4 PSChromaKeyRGBA(VertData v_in) : TARGET
{
	float4 rgba = image.Sample(textureSampler, v_in.uv);
	return ProcessChromaKey(rgba, v_in);
}

//一个shader可以有一个或一个以上的technique,每个technique都有一个唯一的名称,可以通过设置Effect
//类中的currentTechnique属性选择使用哪一个technique
technique Draw
{
	pass
	{
		vertex_shader = VSDefault(v_in);
		//pixel_shader 对给定的模型/对象/一组顶点处理所有像素(逐像素),可以有两个输出值:颜色和深度
		//从vertex shader的输出值获取数据,包括位置、法线、纹理坐标等
		pixel_shader  = PSChromaKeyRGBA(v_in);
	}
}

其中还是有多不懂,需要多花时间去理解和学习,一步一个脚印慢慢来,嘻嘻。

在网上找到一个对shader进行详细解析的文档(XNAShader编程教程.pdf),易于理解shader  effect是如何使用的,希望能帮助到大家。

网盘链接:https://pan.baidu.com/s/182ejzqfubJxBA9P-SpKTPA 
提取码:8y9r 


 

 类似资料: