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渲染中纹理混合的效果了。
Direct2D开发:纹理混合的更多相关文章
- 【Direct2D开发】 通过操作像素实现纹理混合
转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 我们都知道Direct2D可以加载并显示图片,但是不知道你有没有想过,这个2D的图形引擎可以进行纹理混合吗?如果 ...
- UnityShader之固定管线命令Combine纹理混合【Shader资料4】
Combine,纹理混合. 我们先看圣典上给的解释. 纹理在基本的顶点光照被计算后被应用.在着色器中通过SetTexture 命令来完成. SetTexture 命令在片面程序被使用时不会生效:这种模 ...
- Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合
一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器 ...
- 纹理混合遇到的问题 pre-multiplying OpenGL Android iOS
纹理混合遇到的问题 pre-multiplying OpenGL Android iOS Alpha-blending pre-multiplying of texture OpenGL Androi ...
- 浅谈App原生开发、混合开发及HTML5开发的优劣
App混合开发(英文名:Hybrid App),是指在开发一款App产品的时候为了提高效率.节省成本即利用了原生的开发技术还应用了HTML5开发技术,是原生和HTML5技术的混合应用.目前App的开发 ...
- 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 & 纹理混合
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星云 ...
- 原生开发、H5开发、混合移动开发的优缺点
一.原生开发(Native App开发) 原生开发,是在Android.IOS等移动平台上利用官方提供的开发语言.开发类库.开发工具进行App开发.比如Android是利用Java.Eclipse.A ...
- Direct2D开发:绘制网格
转载请注明出处:http://www.cnblogs.com/Ray1024 一.引言 最近在使用Direct2D进行绘制工作中,需要实现使用Direct2D绘制网格的功能.在网上查了很多资料,终于实 ...
- [Direct2D开发] 绘制网格
转载请注明出处:http://www.cnblogs.com/Ray1024 一.引言 最近在使用Direct2D进行绘制工作中,需要实现使用Direct2D绘制网格的功能.在网上查了很多资料,终于实 ...
随机推荐
- 将数据导出到Excel2007格式。
增加数据格式 public static void TableToExcel2(DataTable table, string filename, string sheetname) { XSSFWo ...
- 从一个例子中体会React的基本面
[起初的准备工作] npm init npm install --save react react-dom npm install --save-dev html-webpack-plugin web ...
- json字符串转成 Map/List
package jsonToMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import n ...
- Git中当add错误的时候怎么办?
傻傻分不清楚. “git add .”是我常用的添加命令,添加完后来个“git status ”总是有那么几次发现有不想添加的东西.好多人用reset,nonono,这样不好会有个head错误爆出. ...
- 修改ulimit
ulimit 用于限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小.进程数据块的大小.Shell 进程创建文件的大小.内存锁住的大小.常驻内存集的大小.打开文件描 ...
- PHP - 如何使用XDEBUG来远程调试?
开发的时候我都是使用XDebug在本地调试,但是最近加入一些项目中去,环境太复杂了,要在本地搭建一个开发环境真的太麻烦了,那么我们怎么使用xdebug来远程调试呢? 我这里使用虚拟机搭建了一个模拟环境 ...
- PMBOK/CMM/CMMI/OPM3
1968年为了解决大型软件项目的软件危机,北大西洋公约组织(NATO)提出了“软件工程”这一术语,以改进软件开发设计过程. 1969年美国项目管理协会(PMI)组织成立,从1981年起经过30年的努力 ...
- 适合wordpress中文网站的seo优化插件 DX-Seo
DX-Seo是一款强大的多功能wordpress seo插件,主要功能及其使用方法如下 全局开关设置 启动插件后,进入"DX-Seo"栏目,你可以开启或者关闭其中的所有功能. 自动 ...
- PyCharm 134 单元测试输出大量空行解决方案
在某次BugFix中,某哥们儿在/helper/pycharm/tcunittest.py加了个这: 各位亲们可以把True改为False即可解决大量空行的问题.
- 1. 初探MongoDB —— 介绍、安装和配置
一.写在前面 之前一直以来,都是使用关系型数据库.也很早就听闻,当数据量大的时候,关系型数据库使用起来会有很多问题.诸如查询数据慢,加载花费时间长等等. 也早就耳闻NOSQL(NoSQL = Not ...