近期对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