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

版权声明:本文为博主原创文章,未经博主允许不得转载。 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对象

(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;

猜你喜欢

转载自blog.csdn.net/felicityWSH/article/details/70595044