介绍
本文主要介绍如何通过键盘设置绿幕抠图的阈值,通过设置不同的阈值,看最终的结果。
最终结果
阈值为 0.9
阈值为 0.2
阈值为 0.5
知识点
获取键盘输入
当键盘按下后,会给窗口的回调函数,传入 msg:WM_KEYDOWN 事件,并通过 wparam 参数,传入是哪个键按下了。
然后窗口回调通过事件的方式通知到 Graphics。
将阈值传到 shader 中
directX 中有个常量值 vertex shader 和 pixel shader 都可以设置,说是常量值,但是该值是可以改变的,说常量应该是绘图的时候对于 shader 是一个常量。通过 PSSetConstantBuffers 函数设置常量资源。
代码
事件相关代码
参考 写的,感觉还有优化的空间,目前只支持传入成员函数,后面学完 c++ 模板相关在回来优化。
#pragma once
#include <windows.h>
class Object
{
};
template<typename returnType, typename argsType>
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<class funcType>
void associate(Object* obj, funcType func)
{
m_obj = obj;
m_func[m_totalFunc] = static_cast<pMemFunc>(func);
m_totalFunc++;
}
template<class funcType>
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<std::mutex> 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 = '\0';
*p++ = '\r';
*p++ = '\n';
*p = '\0';
}
OutputDebugString(buf);
}
举例
odprintf("m_threshold %f ", m_threshold);