首页 > 代码库 > 在Unity中使用Direct2D

在Unity中使用Direct2D

在Unity中可能需要在纹理上面绘制文字、图像等。比如游戏中的显示器,手机等等等等等。太多了。

Unity的Textute2D类提供了设置像素的操作,但是这效率实在不敢恭维。


汉字数量巨大,全部贴在一张图上既耗空间,不方便改变字体样式。

使用FreeType2等CPU计算的文字库一帧又画不了多少,毕竟还要提交到显存


于是瞄准了Direct2D,当初学习这图像接口时就被微软说的“能与Direct3D进行完美交互”所吸引。

好在Unity支持DX11了,我们能够在Unity上面使用Direct2D了。所以只能在支持DX11的机器上面运行。


首先得说一下授权许可,毕竟Unity是一款商业软件。

Unity明确指出:Unity Pro才能使用Plug-in,即插件。Unity Free版本是不可以使用的。

但是,它说的是“插件”,而不是“本地代码”。

有童鞋肯定就会说我这是咬文嚼字了。非也非也,“插件”是指遵循当前环境并按照其对接口的相关规定写的代码。


那么,Unity插件有什么规定呢?

需要提供UnitySetGraphicsDevice与UnityRenderEvent

前者获取图形设备信息与图像设备相关事件,后者提供渲染事件。这两个接口是Unity自动调用的(或者也算半自动),

不能显式调用——因为没意义.....


你的本地代码提供了这两个接口就算插件,否则就是普通的本地代码库。

作为本地代码库不能放在plugin文件夹内,不能调用,因为Unity会认为是个插件,而应该直接放在工厂目录下。


再者,假设你写了个插件却放在这个目录下,那么那两个接口不能自动被调用。



好了,就这样可以创建Direct2D了,那个版本的?1.0?1.1?甚至1.2?随意啦!


但是,我建议这样做:

[cpp] view plaincopyprint?
  1. #include "windows.h" 
  2.  
  3. HMODULE WINAPI LoadLibraryWrapA(char* file_name){ 
  4.     return LoadLibraryA(file_name); 
  5.  
  6. HMODULE WINAPI LoadLibraryWrapW(wchar_t* file_name){ 
  7.     return LoadLibraryW(file_name); 
  8.  
  9. BOOL WINAPI FreeLibraryWrap(HMODULE hLibModule){ 
  10.     return FreeLibrary(hLibModule); 
  11.  
  12. FARPROC WINAPI GetProcAddressWrap(HMODULE hModule,char* lpProcName){ 
  13.     return GetProcAddress(hModule, lpProcName); 


面的代码封装成dll文件,这样像C++一样显式调用dll文件。

好处如下:

1. 方便: 不知道是不是Unity的原因,反正假设你想一般dll一样使用Unity C#中的DllImport,

使用后除非关闭Unity,否则不能再次修改dll文件。这简直蛋疼,修改一行代码就需要:

关闭Unity -> 复制dll -> 打开Unity

2. 方便调试,在VS Express 2013 for Windows Desktop中,选择


工具——附加到进程,选择Unity进程


就能直接调试dll文件了,很方便。


使用GetProcAddressWrap获取函数指针之后使用C#中的

Marshal.GetDelegateForFunctionPointer

就能将函数指针换成C#中的托管方法。


比如先定义:

[csharp] view plaincopyprint?
  1. // 初始化 D2D 管理器 
  2. public delegate System.UInt32 D2DManagerInit(); 
  3. public D2DManagerInit m_D2DManagerInit; 

使用:

[csharp] view plaincopyprint?
  1. proc = GetProcAddressWrap(m_unityd2ddll, "D2DManagerInit"); 
  2. m_D2DManagerInit = (D2DManagerInit)Marshal.GetDelegateForFunctionPointer(proc,typeof(D2DManagerInit)); 

之后使用m_D2DManagerInit()就能调用dll文件中的D2DManagerInit函数了。


最后在OnApplicationQuit里面释放即可。




创建 D2D 1.0 的 流程:


首先创建一个公共的  D2D工厂  ,毕竟可能创建多个


Unity中 Texture2D::GetNativeTextureID在DX11环境下就能获取一个ID3D11Texture2D的指针


这个指针能获取     D3D11设备  ,D3D11设备 能获取当前    D3D11设备上下文。


D3D与D2D交互要D3D设备有D3D11_CREATE_DEVICE_BGRA_SUPPORT(D3D11环境下)




D3D11设备   -------> 创建Texture2D,D3D11_TEXTURE2D_DESC参考如下:

[cpp] view plaincopyprint?
  1. sharedTextureDesc.Width = 512; 
  2. sharedTextureDesc.Height = 512; 
  3. sharedTextureDesc.MipLevels = 1; 
  4. sharedTextureDesc.ArraySize = 1; 
  5. sharedTextureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 
  6. sharedTextureDesc.SampleDesc.Count = 1; 
  7. sharedTextureDesc.Usage = D3D11_USAGE_DEFAULT; 
  8. sharedTextureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; 

必须有D3D11_BIND_RENDER_TARGET,否则不能创建渲染目标呈现器。记得保留这个指针。



然后对 D3D11Textue2D 使用QueryInterface获取 Dxgi表面

使用这个Dxgi表面利用D2D工厂CreateDxgiSurfaceRenderTarget创建RT

就好了,记得释放这个Dxgi表面:



渲染D2D

如同一般那样,不过由于变成了dll,可能一些习惯要改成C语言那样。


呈现D2D

渲染完成后,使用

D3D11设备上下文的CopyResource方法就能拷贝渲染结果到目标纹理了:


DirectWrite简直不错:




稍微写点代码就能模仿某游戏那样的效果:


嗯,看来还不够完善,再加油吧:


话说自带的Sprite着色器很不错,简直赞


在Unity中使用Direct2D