本文主要介绍如何通过键盘设置绿幕抠图的阈值,通过设置不同的阈值,看最终的结果。
最终结果阈值为 0.9
阈值为 0.2
阈值为 0.5
当键盘按下后,会给窗口的回调函数,传入 msg:WM_KEYDOWN 事件,并通过 wparam 参数,传入是哪个键按下了。
然后窗口回调通过事件的方式通知到 Graphics。
directX 中有个常量值 vertex shader 和 pixel shader 都可以设置,说是常量值,但是该值是可以改变的,说常量应该是绘图的时候对于 shader 是一个常量。通过 PSSetConstantBuffers 函数设置常量资源。
代码 事件相关代码参考 写的,感觉还有优化的空间,目前只支持传入成员函数,后面学完 c++ 模板相关在回来优化。
#pragma once #include窗口回调class Object { }; template class Event { #define EVENT_LIST_MAX_NUM (10) typedef returnType(Object::* pMemFunc)(argsType arg); public: Event() { m_totalFunc = 0; m_obj = NULL; for (int i = 0; i < EVENT_LIST_MAX_NUM; i++) m_func[i] = NULL; } template void associate(Object* obj, funcType func) { m_obj = obj; m_func[m_totalFunc] = static_cast (func); m_totalFunc++; } template void disAssociate(Object* obj, funcType func) { bool isFind = false; int i = 0; if (obj != m_obj) return; for (i = 0; i < m_totalFunc; i++) { if (m_func[i] == func) { isFind = true; break; } } if (isFind) { for (i; i < m_totalFunc - 1; i++) m_func[i] = m_func[i + 1]; m_func[i] = NULL; m_totalFunc--; } } void sendEvent(argsType arg) { for (int i = 0; i < m_totalFunc; i++) { if (m_func[i] != NULL) ((m_obj->*pMemFunc(m_func[i])))(arg); } } private: Object* m_obj; pMemFunc m_func[EVENT_LIST_MAX_NUM]; int m_totalFunc; };
LRESULT Window::HandleMsg(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
m_winEvent.sendEvent(wparam);
break;
default:
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
创建常量值资源
// constant D3D11_BUFFER_DESC bufferDesc; ZeroMemory(&bufferDesc, sizeof(bufferDesc)); bufferDesc.ByteWidth = 16; // 必须是 16 的倍数,不然不能创建资源 bufferDesc.Usage = D3D11_USAGE_DYNAMIC; bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bufferDesc.MiscFlags = 0; bufferDesc.StructureByteStride = 0; m_pDevice->CreateBuffer(&bufferDesc, NULL, &m_constBuffer); m_pContext->PSSetConstantBuffers(0, 1, &m_constBuffer);更新常量值资源
D3D11_MAPPED_SUBRESOURCE ms;
m_pContext->Map(m_constBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
{
std::lock_guard guard(m_mutex);
memcpy_s(ms.pData, sizeof(m_threshold), &m_threshold, sizeof(m_threshold));
}
m_pContext->Unmap(m_constBuffer, 0);
shader 中使用
Texture2D tex : register(t0);
SamplerState samplerLinear : register(s0);
cbuffer ConstantBuffer : register(b0)
{
float m_threshold;
}
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(m_threshold, dis));
}
其他
窗口时,不能使用 printf 或者 cout 输出信息到屏幕上,调试起来不太方便。对于窗口可以使用 OutputDebugString 函数将数据打印到 输出窗口。下面的函数是对 OutputDebugString 进行了封装。参考
void __cdecl odprintf(const char* format, ...)
{
char buf[4096], * p = buf;
va_list args;
va_start(args, format);
p += _vsnprintf(p, sizeof buf - 1, format, args);
va_end(args);
if (p > buf && isspace(p[-1]))
{
*--p = ' ';
*p++ = 'r';
*p++ = 'n';
*p = ' ';
}
OutputDebugString(buf);
}
举例
odprintf("m_threshold %f ", m_threshold);



