在unity中可能需要在纹理上面绘制文字、图像等。比如游戏中的显示器,手机等等等等等。太多了。 unity的textute2d类提供了设置像素的操作, 但是这效率实在不敢恭维。 汉字数量巨大,全部贴在一张图上既耗空间,不方便改变字体样式。 使用freetype2等cpu计算
在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?
#include windows.h hmodule winapi loadlibrarywrapa(char* file_name){ return loadlibrarya(file_name); } hmodule winapi loadlibrarywrapw(wchar_t* file_name){ return loadlibraryw(file_name); } bool winapi freelibrarywrap(hmodule hlibmodule){ return freelibrary(hlibmodule); } farproc winapi getprocaddresswrap(hmodule hmodule,char* lpprocname){ return getprocaddress(hmodule, lpprocname); } #include windows.hhmodule winapi loadlibrarywrapa(char* file_name){ return loadlibrarya(file_name);}hmodule winapi loadlibrarywrapw(wchar_t* file_name){ return loadlibraryw(file_name);}bool winapi freelibrarywrap(hmodule hlibmodule){ return freelibrary(hlibmodule);}farproc winapi getprocaddresswrap(hmodule hmodule, char* lpprocname){ 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?
// 初始化 d2d 管理器 publicdelegate system.uint32 d2dmanagerinit(); public d2dmanagerinit m_d2dmanagerinit; // 初始化 d2d 管理器 public delegate system.uint32 d2dmanagerinit(); public d2dmanagerinit m_d2dmanagerinit;
使用:
[csharp] view plaincopyprint?
proc = getprocaddresswrap(m_unityd2ddll, d2dmanagerinit); m_d2dmanagerinit = (d2dmanagerinit)marshal.getdelegateforfunctionpointer(proc,typeof(d2dmanagerinit)); proc = getprocaddresswrap(m_unityd2ddll, d2dmanagerinit); 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?
sharedtexturedesc.width = 512; sharedtexturedesc.height = 512; sharedtexturedesc.miplevels = 1; sharedtexturedesc.arraysize = 1; sharedtexturedesc.format = dxgi_format_r8g8b8a8_unorm; sharedtexturedesc.sampledesc.count = 1; sharedtexturedesc.usage = d3d11_usage_default; sharedtexturedesc.bindflags = d3d11_bind_shader_resource | d3d11_bind_render_target; sharedtexturedesc.width = 512; sharedtexturedesc.height = 512; sharedtexturedesc.miplevels = 1; sharedtexturedesc.arraysize = 1; sharedtexturedesc.format = dxgi_format_r8g8b8a8_unorm; sharedtexturedesc.sampledesc.count = 1; sharedtexturedesc.usage = d3d11_usage_default; 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着色器很不错,简直赞