Direct2D开发:纹理混合

转载请注明出处: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渲染中纹理混合的效果了。

时间: 2025-01-09 11:24:40

Direct2D开发:纹理混合的相关文章

火云开发课堂 - 《Shader从入门到精通》系列 第七节:在Shader中实现纹理混合

<Shader从入门到精通>系列在线课程 第七节:在Shader中实现纹理混合 视频地址:http://edu.csdn.net/course/detail/1441/22669?auto_start=1 交流论坛:http://www.firestonegames.com/bbs/forum.php 工程下载地址:请成为正式学员获取工程 课程截图: 版权声明:本文为博主原创文章,未经博主允许不得转载.

【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 &amp; 纹理混合

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/41175585 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 邮箱: [email protected] 本文介绍了Unity中子着色器.通道和标签相关的详细概念与写法,以及纹理的设置方法,基本的纹理混合写法,写了5个Shader作为本文Shader讲解的实战内容,最后创建了一个梦幻的光之

WebRTC原生开发和混合开发优缺点分析对比

WebRTC的出现,让企业快速开发出一个支持全平台的音视频程序成为可能.在WebRTC之前,企业想要开发出一个全平台的音视频程序,难度,工作量非常大.使用WebRTC后,音视频程序中一些通用的模块比如音视频采集,播放模块,rtp,rtcp协议模块,都可以直接复用WebRTC现成的,不用自己再重复造轮子.这些模块开发难度不是很大,但是工作量非常大.使用WebRTC后,企业可以专注于优化流程,解决bug,实现业务需求. 目前,开发WebRTC程序有两种途径 原生开发,自己下载WebRTC代码,每个平

UnityShader之固定管线命令Combine纹理混合【Shader资料4】

Combine,纹理混合. 我们先看圣典上给的解释. 纹理在基本的顶点光照被计算后被应用.在着色器中通过SetTexture 命令来完成. SetTexture 命令在片面程序被使用时不会生效:这种模式下像素操作被完全描述在着色器中. 材质贴图可以用来做老风格的混合器效果.你能在一个通道中使用多个SetTexture 命令 - 所有纹理被顺序的应用,如同绘画程序中的层一样.SetTexture 命令必须放置在通道的末尾 Texture block combine command 纹理块合并命令

Unity3d之Shader编程:子着色器、通道与标签的写法 &amp; 纹理混合

一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器定义了一个渲染通道的列表,并可选是否为所有通道初始化所需要的通用状态.子着色器的写法如下: Subshader{ [Tags] [CommonState] Passdef [Passdef ...] } 也就是通过可选标签,通用状态 和 一个Pass 定义的列表构成了子着色器. 当Unity选择用于

html5开发移动混合App系列1-开发环境搭建

最近公司准备开发门店收银系统,是基于IPAD的程序,决定采用基于 Ionic + Cordova + AngularJS技术混合开发模式. 准备 一台mac(安装了mac os的虚拟机也可以),nodejs,ionic,xcode 安装 1,安装nodejs 到官网下载nodejs安装包(pkg文件),需要0.10.*及以下的版本,高版本会有很多插件不可用.我使用的版本是v0.10.38(下载地址:http://nodejs.org/dist/v0.10.38/  ) ,下载完成之后直接打开按提

Direct2D开发:向 MFC 项目添加 Direct2D 对象

目的: 介绍如何将基本 Direct2D (D2D) 对象添加到 Visual C++ Microsoft 基础类库 (MFC) 项目中,然后将该项目构建到在渐变背景上输出"Hello, world"的应用程序中. 此演练演示如何完成以下这些任务: 创建 MFC 应用程序. 创建一个纯色画笔和一个线性渐变画笔. 修改渐变画笔,以便在调整窗口大小时它会相应更改. 实现 Direct2D绘图处理程序. 验证结果. 系统必备: 若要完成本演练,需要 Visual Studio. 创建 MFC

html5开发移动混合App系列2-开发环境搭建(windows)

Java下载: http://www.oracle.com/technetwork/java/javase/downloads/index.html 环境变量: JAVA_HOME=C:\Program Files\Java\jdk1.8.0_25 PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; CLASSPATH=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 测试: javac 安装nodejs no

手机游戏开发中如何选择适合的纹理格式

为毛要写这个 本来觉得像这样的问题,是无法归类的,因为不同的项目有不同的需求,但今天因为quick论坛中的一个技术疑问贴,钩起了我整理这篇文章的兴趣 http://www.cocoachina.com/bbs/read.php?tid=214811 于是,我决定尽力描述一下纹理格式选择方面的问题,一是起到一个科普的作用,因为目前没有发现十分完整的讲这方面的文章.二是整理一下自己的思路. 当然,这些东西肯定不是我自己凭空YY出来的,我也是参考了不少文章,也从项目中总结了一些问题.在此先列出一些链接