版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/felicityWSH/article/details/70595044
注意:阅读本文时,需要读者具有D3D9纹理渲染的基础。
红龙书第六章,介绍了纹理映射的知识。但是运行书中的纹理矩形demo(工程名TexQuad)时,发现图片显示模糊,尝试修改D3DSAMP_MINFILTER和D3DSAMP_MAGFILTER的值后 效果依旧不理想 。
经过调查与编码验证,发现显示图片时如果使用离屏表面,就不会有以上问题。书中TexQuad工程的逻辑就不阐述了 感兴趣的可以自己研究一下,本文重点介绍离屏表面的方法。
需要定义的相关变量如下:
HWND m_hPreview; // 预览窗口的句柄
SIZE m_szD3DResolution; // 初始化D3D设备的宽高 (通常使用显示器的宽高 如果有多个显示器 则使用最大的宽高)
LPDIRECT3D9 m_pDirect3D9; // D3D9对象
LPDIRECT3DDEVICE9 m_pDevice9; // 宽高使用m_szD3DResolution
LPDIRECT3DTEXTURE9 m_pTexture9; // 宽高使用m_szD3DResolution
LPDIRECT3DVERTEXBUFFER9 m_pVertexBuffer9; // 顶点缓冲区
SIZE m_szVideoResolution; // 视频数据的宽高(与预览窗口宽高无关)
LPDIRECT3DSURFACE9 m_pOffscreenSurface; // 缓冲离屏表面,临时保存一帧图像的数据 宽高使用m_szVideoResolution
本文重点介绍:初始化D3D、渲染一帧图像。(文中只会提及与离屏表面相关的逻辑,需要读者具有纹理渲染的基础)。
1、初始化D3D对象
注意需要调用 如下代码:
m_pDevice9->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pDevice9->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
(2)初始化离屏表面m_pOffscreenSurface(宽高使用视频帧的宽高值)
D3DDISPLAYMODE dm = {};
m_pDirect3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm);
m_pDevice9->CreateOffscreenPlainSurface(m_szVideoResolution.cx,
m_szVideoResolution.cy,
dm.Format,
D3DPOOL_DEFAULT,
&m_pOffscreenSurface,
NULL);
2、渲染一帧图像
(1)获取到一帧图像, 将数据写入到离屏表面m_pOffscreenSurface中
关键词:LPDIRECT3DSURFACE9::LockRect、memcpy、LPDIRECT3DSURFACE9::UnlockRect
BOOL CD3D9Render::WriteOffscreenSurface(BYTE* pData)
{
if (!m_bInited || !pData)
return FALSE;
D3DLOCKED_RECT lockRect = {};
if (FAILED(m_pOffscreenSurface->LockRect(&lockRect, NULL, 0)))
return FALSE;
BYTE* pDest = (BYTE*)lockRect.pBits;
int nStride = m_szVideoResolution.cx * 4; // 4 : ARGB
assert(lockRect.Pitch == nStride);
for (LONG i = 0; i < m_szVideoResolution.cy; i++)
{
memcpy(pDest + (i * nStride),
pData + (i * nStride),
nStride);
}
m_pOffscreenSurface->UnlockRect();
return TRUE;
}
(2)将离屏表面的数据,复制到m_pTexture9
关键词: LPDIRECT3DTEXTURE9::GetSurfaceLevel、LPDIRECT3DDEVICE9::StretchRect
RECT rcSrc = {0, 0, m_szVideoResolution.cx, m_szVideoResolution.cy};
LPDIRECT3DSURFACE9 pSurface9 = NULL;
m_pTexture9->GetSurfaceLevel(0, &pSurface9);
CRect rcDst;
GetClientRect(m_hPreview, &rcDst);
// 如果预览窗口的宽高 大于初始化D3D时的宽高 StretchRect会调用失败
assert(rcDst.width <= m_szD3DResolution.cx && rcDst.height <= m_szD3DResolution.cy);
m_pDirect3DDevice9->StretchRect(m_pOffscreenSurface, &rcSrc, // source
pSurface9, &rcDst, // dest
D3DTEXF_LINEAR);
(3)实际的渲染函数调用,逻辑与普通的纹理渲染是相同的(可以参考TexQuad的逻辑)
m_pDevice9->BeginScene
m_pDevice9->SetTexture(0, m_pTexture9)
m_pDevice9->DrawPrimitive
m_pDevice9->EndScene
m_pDevice9->Present
注意
如果预览窗口的宽或高,大于初始化D3D对象时的宽高(即m_szD3DResolution),那么调用LPDIRECT3DDEVICE9::StretchRect时会失败。
所以要满足一个要求:m_hPreview.width <= m_szD3DResolution.cx && m_hPreview.height <= m_szD3DResolution.cy。
通常可以使用最大显示器的宽高来初始化D3D,因为一般来说,很少有应用程序中预览窗口的宽高,会大于显示器宽高的情况,或者使用如下方法获取的宽高:
D3DDISPLAYMODE d3ddm;
HRESULT hr = LPDIRECT3D9::GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
if(FAILED(hr))
return FALSE;
D3DPRESENT_PARAMETERS::BackBufferWidth = d3ddm.Width;
D3DPRESENT_PARAMETERS::BackBufferHeight = d3ddm.Height;