转载请注明出处:http://www.cnblogs.com/Ray1024

一、概述

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

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

二、思路解析

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

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

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

1.IWICBitmap::Lock函数介绍

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

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

  1. HRESULT GetDataPointer(
  2. [out] UINT *pcbBufferSize,
  3. [out] BYTE **ppbData
  4. );
  5.  
  6. 功能:获取锁定矩形中左上角像素的指针
  7. 参数:
  8. pcbBufferSize [out] 获取内存大小
  9. ppbData [out] 获取内存数据

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

三、纹理混合实现

1.加载IWICBitmap对象

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

  1. ID2D1Bitmap* pBitmap = NULL;
  2. IWICBitmapDecoder* pDecoder = NULL;
  3. IWICBitmapFrameDecode* pSource = NULL;
  4. IWICBitmap* pWIC = NULL;
  5. IWICFormatConverter* pConverter = NULL;
  6. IWICBitmapScaler* pScaler = NULL;
  7. UINT originalWidth = 0;
  8. UINT originalHeight = 0;
  9.  
  10. // 1.加载IWICBitmap对象
  11.  
  12. HRESULT hr = pIWICFactory->CreateDecoderFromFilename(
  13. uri,
  14. NULL,
  15. GENERIC_READ,
  16. WICDecodeMetadataCacheOnLoad,
  17. &pDecoder
  18. );
  19.  
  20. if (SUCCEEDED(hr))
  21. {
  22. hr = pDecoder->GetFrame(0, &pSource);
  23. }
  24.  
  25. if (SUCCEEDED(hr))
  26. {
  27. hr = pSource->GetSize(&originalWidth,&originalHeight);
  28. }
  29.  
  30. if (SUCCEEDED(hr))
  31. {
  32. hr = pIWICFactory->CreateBitmapFromSourceRect(
  33. pSource, 0,0,(UINT)originalWidth,(UINT)originalHeight, &pWIC);
  34. }

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

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

  1. // 2.从IWICBitmap对象读取像素数据
  2. IWICBitmapLock *pILock = NULL;
  3. WICRect rcLock = { 0, 0, originalWidth, originalHeight };
  4. hr = pWIC->Lock(&rcLock, WICBitmapLockWrite, &pILock);
  5.  
  6. if (SUCCEEDED(hr))
  7. {
  8. UINT cbBufferSize = 0;
  9. BYTE *pv = NULL;
  10.  
  11. if (SUCCEEDED(hr))
  12. {
  13. // 获取锁定矩形中左上角像素的指针
  14. hr = pILock->GetDataPointer(&cbBufferSize, &pv);
  15. }

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

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

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

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

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

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

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

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

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

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

  1. // 5.使用IWICBitmap对象创建D2D位图
  2.  
  3. if (SUCCEEDED(hr))
  4. {
  5. hr = pIWICFactory->CreateFormatConverter(&pConverter);
  6. }
  7. if (SUCCEEDED(hr))
  8. {
  9. hr = pIWICFactory->CreateBitmapScaler(&pScaler);
  10. }
  11. if (SUCCEEDED(hr))
  12. {
  13. hr = pScaler->Initialize(pWIC, (UINT)originalWidth, (UINT)originalHeight, WICBitmapInterpolationModeCubic);
  14. }
  15. if (SUCCEEDED(hr))
  16. {
  17. hr = pConverter->Initialize(
  18. pScaler,
  19. GUID_WICPixelFormat32bppPBGRA,
  20. WICBitmapDitherTypeNone,
  21. NULL,
  22. 0.f,
  23. WICBitmapPaletteTypeMedianCut
  24. );
  25. }
  26.  
  27. if (SUCCEEDED(hr))
  28. {
  29. hr = pRenderTarget->CreateBitmapFromWicBitmap(
  30. pConverter,
  31. NULL,
  32. &pBitmap
  33. );
  34. }

6.显示

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

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

  1. // 创建位图
  2. if (SUCCEEDED(hr))
  3. {
  4. LoadBitmapFromFile(m_pRT,m_pWICFactory, L"bitmap.png",0,0, &m_pBitmap);
  5. }
  6.  
  7. // 创建位图,并进行颜色混合
  8. if (SUCCEEDED(hr))
  9. {
  10. // 从文件创建WIC位图,将WIC位图进行颜色混合,之后创建D2D位图
  11. m_pBitmapBlended = GetBlendedBitmapFromFile(m_pWICFactory, m_pRT, L"bitmap.png", D2D1::ColorF(1, 0, 0, 1));//红色
  12. m_pBitmapBlended1 = GetBlendedBitmapFromFile(m_pWICFactory, m_pRT, L"bitmap.png", D2D1::ColorF(0, 1, 0, 1));//绿色
  13. m_pBitmapBlended2 = GetBlendedBitmapFromFile(m_pWICFactory, m_pRT, L"bitmap.png", D2D1::ColorF(0, 0, 1, 1));//蓝色
  14. }

结果演示图如下:

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

四、扩展延伸

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

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

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

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

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

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

  5.显示。

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

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

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

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

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

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

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

  1. for (unsigned int i=0; i<cbBufferSize; i+=4)
  2. {
  3. if (pv[i+3] != 0)
  4. {
  5. pv[i] = pv[i]*(1-proportion) + pv1[i]*proportion;
  6. pv[i+1] = pv[i+1]*(1-proportion) + pv1[i+1]*proportion;
  7. pv[i+2] = pv[i+2]*(1-proportion) + pv1[i+2]*proportion;
  8. pv[i+3] = pv[i+3]*(1-proportion) + pv1[i+3]*proportion;
  9. }
  10. }

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

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

五、结语

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

【Direct2D开发】 通过操作像素实现纹理混合的更多相关文章

  1. Direct2D开发:纹理混合

    转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 我们都知道Direct2D可以加载并显示图片,但是不知道你有没有想过,这个2D的图形引擎可以进行纹理混合吗?如果 ...

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

    Combine,纹理混合. 我们先看圣典上给的解释. 纹理在基本的顶点光照被计算后被应用.在着色器中通过SetTexture 命令来完成. SetTexture 命令在片面程序被使用时不会生效:这种模 ...

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

    一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器 ...

  4. iOS 开发:绘制像素到屏幕

    转载:https://segmentfault.com/a/1190000000390012 译注:这篇文章虽然比较长,但是里面的内容还是很有价值的. 像素是如何绘制到屏幕上面的?把数据输出到屏幕的方 ...

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

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星云 ...

  6. 纹理混合遇到的问题 pre-multiplying OpenGL Android iOS

    纹理混合遇到的问题 pre-multiplying OpenGL Android iOS Alpha-blending pre-multiplying of texture OpenGL Androi ...

  7. loadrunner 脚本开发-执行操作系统命令

    脚本开发-执行操作系统命令 by:授客 QQ:1033553122 思路: 用loadrunner system()函数 函数原型: int system( const char *string ); ...

  8. Android开发---如何操作资源目录中的资源文件4 ---访问xml的配置资源文件的内容

    Android开发---如何操作资源目录中的资源文件4 XML,位于res/xml/,这些静态的XML文件用于保存程序的数据和结构. XmlPullParser可以用于解释xml文件 效果图: 描述: ...

  9. Android开发---如何操作资源目录中的资源文件3--圆角边框、背景颜色渐变效果、边框颜色

    Android开发---如何操作资源目录中的资源文件3 效果图 1.圆角边框 2.背景颜色渐变效果 1.activity_main.xml 描述: 定义了一个shape资源管理按钮 <?xml ...

随机推荐

  1. iOS仿微博客户端一条微博的布局

    前言 做一个微博客户端的第三方是自学的第一个实践的项目,自从从事iOS工作之后,就把这个项目给搁置了.趁现在过年回来有些空闲时间,再次修改(总觉得项目就是不停地修改).并且记录一点东西,以后可再回头看 ...

  2. 了解 : angular controller link ng-init 顺序

    controller 会先跑,接着是view 里的ng-init,最后是link (指令里的). 所有在指令里如果用link去拿$attr,会有拿不到ng-init想setup的值

  3. 《Shell脚本学习指南》学习笔记之变量、判断和流程控制

    变量 定义变量 可以使用export和readonly来设置变量,export用于修改或打印环境变量,readonly则使得变量不得修改.语法: export name[=word] ... read ...

  4. win7下使用git

    1 安装git for window 2 安装tortoiseGit 3 生成public key 3.1 打开git bash 3.2 创建~/.ssh文件夹 mkdir ~/.ssh 3.3 配置 ...

  5. .NET获取客户端的操作系统、IP地址、浏览器版本

    获取客户端的操作系统: #region 获取操作系统版本号 /// <summary> /// 获取操作系统版本号 /// </summary> /// <returns ...

  6. Java基础(下)(JVM、API)

    Java基础(下) 第三部分:Java源程序的编辑 我们知道,计算机是不能直接理解源代码中的高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言编写的程序. 翻译 ...

  7. Objective-c日记-之属性列表

    属性列表 1,概述 在Cocoa中,有一类名为属性列表的对象(常简写为plist),Cocoa知道如何将它们保存到文件和从文件中加载.包括以下类NSArray,NSDictionary,NSStrin ...

  8. Codevs2776 寻找代表元

    2776 寻找代表元 时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold    题目描述 Description 广州二中苏元实验学校一共有n个社团,分别用1到n编号 ...

  9. 1635: [Usaco2007 Jan]Tallest Cow 最高的牛

    1635: [Usaco2007 Jan]Tallest Cow 最高的牛 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 383  Solved: 211 ...

  10. SQL中PIVOT和UNPIVOT行列转换

    DECLARE @sql_col VARCHAR(8000); DECLARE @sql_str VARCHAR(8000); DECLARE @sql_ VARCHAR(MAX); SELECT @ ...