您的位置:首页 > 其它

[D3D9] 红龙书 - 优化纹理映射的源码 (源码渲染纹理的效果很模糊)

2017-04-24 13:28 417 查看
注意:阅读本文时,需要读者具有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对象

(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,因为一般来说,很少有应用程序中预览窗口的宽高,会大于显示器宽高的情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息