介绍
绿幕抠图指将图片中的绿色扣去,并将该处的 alpha 分量设为 0,并将另外一张图片作为背景,以实现背景替换的功能,常用于电影和电视的制作场景。
最终效果
原始图片

绿幕抠图后效果

代码
这一块的代码改动,主要在像素着色器 shader。
ps shader
本文只是实现了最简单的绿幕抠图,对于这种比较完美的图片,抠图效果较好,对于其他图片还有较大的优化空间。
Texture2D tex : register(t0);
SamplerState samplerLinear : register(s0);
struct VSOut
{
float4 pos : SV_Position;
float2 tex : TEXCOORD1;
};
float4 MyPs(VSOut pIn) : SV_Target
{
float4 sampleColor = tex.Sample(samplerLinear, pIn.tex);
float dis = distance(float3(sampleColor.xyz), float3(0.0, 1.0, 0.0));
return float4(sampleColor.xyz, step(0.4, dis));
}
原理
本文使用的绿幕抠图的原理比较简单,就是比较该点的 rgb 颜色和绿色之间的差距d,并设置一个阈值a,但差距小于阈值 a,则认为其他绿色,将其透明分量设为 0。
step
step(x,y) 当 x ≤y时,返回1,否则为 0。
具体代码
void Graphics::DrawPicture()
{
InitEffect();
/************************************* 输入装配阶段 **************************************/
//创建顶点缓存
struct SimpleVertex
{
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT2 tex;
};
SimpleVertex vertices[] =
{
{DirectX::XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT2(0.0f, 1.0f)},
{DirectX::XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT2(0.0f, 0.0f)},
{DirectX::XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT2(1.0f, 0.0f)},
{DirectX::XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT2(1.0f, 1.0f)},
};
D3D11_BUFFER_DESC verticsDesc = {};
verticsDesc.ByteWidth = sizeof(vertices) * 5; // 字节数
// 将 usage 设为 D3D11_USAGE_IMMUTABLE D3D11_USAGE_DEFAULT 可行
verticsDesc.Usage = D3D11_USAGE_IMMUTABLE; // 资源的使用,gpu和cpu 的读写权限
verticsDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; // 标识如何将资源绑定到 pipeline
verticsDesc.CPUAccessFlags = 0; // CPU 的读写权限
verticsDesc.MiscFlags = 0;
verticsDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA resourceData = {};
resourceData.pSysMem = vertices;
resourceData.SysMemPitch = 0;
resourceData.SysMemSlicePitch = 0;
ID3D11Buffer* verticesBuffer = NULL;
m_pDevice->CreateBuffer(&verticsDesc, &resourceData, &verticesBuffer);
UINT strider = sizeof(SimpleVertex);
UINT offset = 0;
m_pContext->IASetVertexBuffers(
0, // start slot
1, // buffer 数量 (start slot ~ start slot + buffer number
&verticesBuffer,// 顶点缓存
&strider, // 每组数据的字节数
&offset); // 偏移量
DirectX::XMUINT3 index[] = {
{0, 1, 2},
{0, 2, 3}};
D3D11_BUFFER_DESC indexDesc = {};
indexDesc.ByteWidth = sizeof(index) * 2; // 字节数
// 将 usage 设为 D3D11_USAGE_IMMUTABLE D3D11_USAGE_DEFAULT 可行
indexDesc.Usage = D3D11_USAGE_IMMUTABLE; // 资源的使用,gpu和cpu 的读写权限
indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; // 标识如何将资源绑定到 pipeline
indexDesc.CPUAccessFlags = 0; // CPU 的读写权限
indexDesc.MiscFlags = 0;
indexDesc.StructureByteStride = 0;
resourceData.pSysMem = index;
resourceData.SysMemPitch = 0;
resourceData.SysMemSlicePitch = 0;
ID3D11Buffer* indexBuffer = NULL;
m_pDevice->CreateBuffer(&indexDesc, &resourceData, &indexBuffer);
// 设置所引缓存
m_pContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// 输入布局
m_pContext->IASetInputLayout(m_inputLayout);
// 图元拓扑结构
m_pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
/************************************* 顶点着色器阶段 **************************************/
m_pContext->VSSetShader(m_pVertexShader, nullptr, 0);
/************************************* 像素着色器阶段 **************************************/
m_pContext->PSSetShader(m_pPixelShader, nullptr, 0);
ID3D11Resource* inputResource = NULL;
ID3D11ShaderResourceView* shaderResourceView = NULL;
std::wstring path = L"res\\image\\tiger.png";
CreateWICTextureFromFile(m_pDevice,
path.c_str(),
&inputResource,
&shaderResourceView);
m_pContext->PSSetShaderResources(0, 1, &shaderResourceView);
ID3D11SamplerState* sampler;
D3D11_SAMPLER_DESC sampleDesc = {};
sampleDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampleDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; // 平铺整数个
sampleDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampleDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampleDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampleDesc.MinLOD = 0;
sampleDesc.MaxLOD = D3D11_FLOAT32_MAX;
m_pDevice->CreateSamplerState(&sampleDesc, &sampler);
m_pContext->PSSetSamplers(0, 1, &sampler);
/************************************* 输出阶段 **************************************/
// 设置渲染目标
m_pContext->OMSetRenderTargets(1, &m_pRenderTargetView, NULL);
ID3D11BlendState* blendState;
D3D11_BLEND_DESC blendDesc = {};
blendDesc.AlphaToCoverageEnable = FALSE;
blendDesc.IndependentBlendEnable = FALSE;
blendDesc.RenderTarget->BlendEnable = TRUE; // 是否开启混合
blendDesc.RenderTarget->SrcBlend = D3D11_BLEND_SRC_ALPHA; // 将源图的 alpha 作为 src rgb 的混合因子
blendDesc.RenderTarget->DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // 将源图的 1-alpha 作为 dst rgb 的混合因子
blendDesc.RenderTarget->BlendOp = D3D11_BLEND_OP_ADD; // 进行相加操作
blendDesc.RenderTarget->SrcBlendAlpha = D3D11_BLEND_ONE; //
blendDesc.RenderTarget->DestBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget->BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; // 可以写入的位置
m_pDevice->CreateBlendState(&blendDesc, &blendState);
const FLOAT BlendFactor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
m_pContext->OMSetBlendState(blendState, BlendFactor, 0xffffffff);
// 设置视口
D3D11_VIEWPORT viewPort = {};
viewPort.TopLeftX = 0;
viewPort.TopLeftY = 0;
viewPort.Width = 800;
viewPort.Height = 600;
viewPort.MinDepth = 0.0f;
viewPort.MaxDepth = 1.0f;
m_pContext->RSSetViewports(1, &viewPort);
// 开始绘制
m_pContext->DrawIndexed(6, 0, 0);
}
参考文献
https://blog.youkuaiyun.com/grace_yi/article/details/109081046

本文介绍了绿幕抠图的基本原理和简单实现,通过比较像素颜色与绿色的差距来设定阈值,实现背景替换。文中提供了一段基于DirectX的像素着色器代码,展示了如何在图形渲染过程中应用此技术。此外,还讨论了代码的优化空间和实际应用中的效果。
2754

被折叠的 条评论
为什么被折叠?



