• 热门专题

Direct2D开发:纹理混合

作者:郭小雷  发布日期:2016-12-22 20:36:28
Tag标签:纹理  
  • 转载请注明出处:http://www.cnblogs.com/Ray1024

    一、概述

    我们都知道Direct2D可以加载并显示图片,但是不知道你有没有想过,这个2D的图形引擎可以进行纹理混合吗?如果可以进行纹理混合,那我们2D的图形引擎就可以做更多的事情,比如蒙版这个概念,我们可以对图片进行更加丰富的操作。

    接触过3D渲染知识的人都知道着色器这个东西,在3D渲染中,着色器分为顶点着色器和像素着色器,这里我们主要实现的是类似于3D渲染中的像素着色器的功能,即纹理(图片)混合。

    二、思路解析

    在Direct2D中想要实现纹理(图片)混合的功能,我们就可以考虑,如果我们可以读写纹理(图片)的每个像素的color数据,那就可以实现纹理(图片)混合的功能。

    但是我们如何来操作(读写)图片的像素数据呢?

    因为Direct2D加载图片是用windows图像处理组件(WIC),我在WIC的MSDN文档中找到了方法。

    1.IWICBitmap::Lock函数介绍

    HRESULT Lock(
      [in]  const WICRect        *prcLock,
      [in]        DWORD          flags,
      [out]       IWICBitmapLock **ppILock
    );
    
    功能 :提供对位图的矩形区域的访问
    参数 :
    	prcLock [in]	要访问的矩形区域
    	flags 	[in]	访问模式(读/写)
    	ppILock [out]	接收锁定的内存位置的指针,IWICBitmapLock类型
    返回 :如果成功,返回S_OK
    

    2.关于IWICBitmapLock类型,我们介绍它的一个成员函数:

    HRESULT GetDataPointer(
      [out] UINT *pcbBufferSize,
      [out] BYTE **ppbData
    );
    
    功能:获取锁定矩形中左上角像素的指针
    参数:
    	pcbBufferSize 	[out]	获取内存大小
    	ppbData 		[out]	获取内存数据

    接下来,我们将详细介绍实现纹理混合的过程。

    三、纹理混合实现

    1.加载IWICBitmap对象

    这一步相信大家都很熟悉了,因为每次创建D2D位图都必须经过这一步操作。直接上代码:

    ID2D1Bitmap*			pBitmap		= NULL;
    IWICBitmapDecoder*		pDecoder	= NULL;
    IWICBitmapFrameDecode*	pSource		= NULL;
    IWICBitmap*				pWIC		= NULL;
    IWICFormatConverter*	pConverter	= NULL;
    IWICBitmapScaler*		pScaler		= NULL;
    UINT					originalWidth	= 0;
    UINT					originalHeight	= 0;
    
    // 1.加载IWICBitmap对象
    
    HRESULT hr = pIWICFactory->CreateDecoderFromFilename(
    	uri,
    	NULL,
    	GENERIC_READ,
    	WICDecodeMetadataCacheOnLoad,
    	&pDecoder
    	);
    
    if (SUCCEEDED(hr))
    {
    	hr = pDecoder->GetFrame(0, &pSource);
    }
    
    if (SUCCEEDED(hr))
    {
    	hr = pSource->GetSize(&originalWidth,&originalHeight);
    }
    
    if (SUCCEEDED(hr))
    {
    	hr = pIWICFactory->CreateBitmapFromSourceRect(
    		pSource, 0,0,(UINT)originalWidth,(UINT)originalHeight, &pWIC);
    }
    

    2.从IWICBitmap对象读取像素数据

    先从WIC位图创建IWICBitmapLock对象,然后从IWICBitmapLock获取图片像素数据的指针,代码如下:

    	// 2.从IWICBitmap对象读取像素数据
    	IWICBitmapLock *pILock = NULL;
    	WICRect rcLock = { 0, 0, originalWidth, originalHeight };
    	hr = pWIC->Lock(&rcLock, WICBitmapLockWrite, &pILock);
    
    	if (SUCCEEDED(hr))
    	{
    		UINT cbBufferSize = 0;
    		BYTE *pv = NULL;
    
    		if (SUCCEEDED(hr))
    		{
    			// 获取锁定矩形中左上角像素的指针
    			hr = pILock->GetDataPointer(&cbBufferSize, &pv);
    		}
    

    3.进行纹理混合的像素计算

    对获取到的图片像素数据进行像素计算,代码如下:

    		// 3.进行纹理混合的像素计算
    		for (unsigned int i=0; i<cbBufferSize; i+=4)
    		{
    			if (pv[i+3] != 0)
    			{
    				pv[i]	*=color.b;
    				pv[i+1]	*=color.g;
    				pv[i+2]	*=color.r;
    				pv[i+3] *=color.a;
    			}
    		}
    

    在上面代码中,需要注意的是像素计算的方法为颜色color的分量相乘。

    还有,细心的朋友可以看出,像素数据的步长为4,每个步长内的4个数组成一个像素完整的颜色值,并且颜色格式为BGRA格式,每一个颜色的取值范围为0.f~1.f。

    4.颜色混合操作结束,释放IWICBitmapLock对象

    纹理混合计算结束后,调用Rlease函数释放IWICBitmapLock对象,即可将计算后的图片像素数据写入IWICBitmap对象即WIC位图,如下:

    		// 4.颜色混合操作结束,释放IWICBitmapLock对象
    		pILock->Release();

    5.使用WIC位图创建D2D位图

    到现在为止,真正意义上的纹理混合的像素数据的读取、计算和写入就完成了。我们直接使用WIC位图创建D2D位图即可,如下:

    // 5.使用IWICBitmap对象创建D2D位图
    
    if (SUCCEEDED(hr))
    {
    	hr = pIWICFactory->CreateFormatConverter(&pConverter);
    }
    if (SUCCEEDED(hr))
    {
    	hr = pIWICFactory->CreateBitmapScaler(&pScaler);
    }
    if (SUCCEEDED(hr))
    {
    	hr = pScaler->Initialize(pWIC, (UINT)originalWidth, (UINT)originalHeight, WICBitmapInterpolationModeCubic);
    }
    if (SUCCEEDED(hr))
    {
    	hr = pConverter->Initialize(
    		pScaler,
    		GUID_WICPixelFormat32bppPBGRA,
    		WICBitmapDitherTypeNone,
    		NULL,
    		0.f,
    		WICBitmapPaletteTypeMedianCut
    		);
    }
    
    if (SUCCEEDED(hr))
    {
    	hr = pRenderTarget->CreateBitmapFromWicBitmap(
    		pConverter,
    		NULL,
    		&pBitmap
    		);
    }
    

    6.显示

    上面的一系列纹理混合操作结束后,我们就可以将混合之后的纹理绘制到窗口显示了。

    在我们这个例子中,创建了4个ID2D1Bitmap对象即D2D位图,m_pBitmap为原图的位图,m_pBitmapBlended、m_pBitmapBlended1、m_pBitmapBlended2分别为原图和红色、绿色、蓝色进行纹理混合之后的位图,创建代码如下(GetBlendedBitmapFromFile函数为我们上面介绍的所有步骤):

    		// 创建位图
    		if (SUCCEEDED(hr))
    		{
    			LoadBitmapFromFile(m_pRT,m_pWICFactory, L'bitmap.png',0,0, &m_pBitmap);
    		}
    
    		// 创建位图,并进行颜色混合
    		if (SUCCEEDED(hr))
    		{
    			// 从文件创建WIC位图,将WIC位图进行颜色混合,之后创建D2D位图
    			m_pBitmapBlended = GetBlendedBitmapFromFile(m_pWICFactory, m_pRT, L'bitmap.png', D2D1::ColorF(1, 0, 0, 1));//红色
    			m_pBitmapBlended1 = GetBlendedBitmapFromFile(m_pWICFactory, m_pRT, L'bitmap.png', D2D1::ColorF(0, 1, 0, 1));//绿色
    			m_pBitmapBlended2 = GetBlendedBitmapFromFile(m_pWICFactory, m_pRT, L'bitmap.png', D2D1::ColorF(0, 0, 1, 1));//蓝色
    		}

    结果演示图如下:

    前面只介绍了纹理混合的重要代码,其余代码就不列出了,有兴趣的朋友可以点击此处下载,源码为D2DBitmapBlend。

    四、扩展延伸

    上面介绍完Direct2D中的纹理混合操作,但是还是比较简单的操作,因为它只对纹理进行颜色混合。

    其实,我们还可以进行纹理之间的混合操作。原理很简单,如下:

      1.创建叠加纹理,读取像素数据;

      2.创建主纹理,读取叠加像素数据;

      3.使用主像素数据和叠加像素数据行混合操作;

      4.使用计算后的主纹理WIC位图创建D2D位图;

      5.显示。

    注意,两个纹理进行混合的计算方法很重要,这需要借鉴3D渲染中的线性插值法进行纹理混合。

    接触过3D渲染的朋友都会知道,3D渲染中,纹理混合的计算方式原理为线性插值,比如GLSL中mix函数,如下:

    genType mix (genType x, genType y, float a)
    

    最终的片段颜色值由mix函数将两者进行混合后得到。mix这个函数是GLSL中一个特殊的线性插值函数,前两个参数分别为主纹理和叠加纹理的像素数据,第三个参数为纹理混合中的叠加纹理所占的比例,计算原理如下:

      x和y混合之后 = x⋅(1−a)+y⋅a

    这就是我们用到的纹理混合的计算原理。

     

    我们现在进行2个纹理混合的操作,这里我只贴上纹理混合的线性混合计算的部分:

    		for (unsigned int i=0; i<cbBufferSize; i+=4)
    		{
    			if (pv[i+3] != 0)
    			{
    				pv[i]	= pv[i]*(1-proportion)	 + pv1[i]*proportion;
    				pv[i+1]	= pv[i+1]*(1-proportion) + pv1[i+1]*proportion;
    				pv[i+2]	= pv[i+2]*(1-proportion) + pv1[i+2]*proportion;
    				pv[i+3] = pv[i+3]*(1-proportion) + pv1[i+3]*proportion;				
    			}
    		}
    

    上面计算部分的proportion为叠加纹理占的比例,这个参数是纹理混合中必不可少的部分。其余代码省略,有兴趣的朋友可以点击此处下载,源码为D2DBitmapBlendWithBitmap。

     

    这是两个纹理混合后的效果如下:

    五、结语

    Direct2D中的纹理混合过程到这里就全部介绍完了。这样我们使用Direct2D也可以达到3D渲染中纹理混合的效果了。

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规