跨平台移动应用开发的渲染解决方案综述
在移动设备上做应用/游戏开发,实现跨平台是主流。对于应用/游戏的逻辑功能和核心算法部分,如果是用C++进行开发的,因为iOS/Android/Windows Phone 8/Windows Store均支持Native的C++开发,所以可以轻松的移植到各个平台,应用/游戏的Backend也有如Photon这样的跨平台解决方案。对于一部分图形要求很高的应用,如游戏和一些UI定制化需求很多的应用,渲染部分往往需要花更多的精力实现移植。对于渲染部分,跨平台开发者有以下三种选择:
1) 使用已有的一些跨平台图形解决方案,如cocos2D-x, Unity 3D等游戏引擎以及HTML5。
2) 将渲染部分在程序设计时独立,针对不同的平台分别实现渲染。在Windows Phone / Windows Store应用中,DirectX和XAML可以结合使用,从而方便的与第三方SDK结合使用。
3) 将现有的渲染代码通过工具映射到其他平台。如开源的Angle project可以将OpenGL ES自动映射到DirectX。
本文将分别对这三种选择依次作介绍,为开发者顺利完成应用/游戏在渲染部分的移植作一个参考。因时间和能力有限,谬误之处请不吝指教。
(1) 使用游戏引擎/HTML5等现有的跨平台解决方案
cocos2d-x:cocos2d-x是一个开源免费、易学易用的2D跨平台游戏引擎,支持Windows Phone, Windows 8, iOS和Android多个平台。目前是国内最流行的2D游戏引擎。其下载地址为:
http://www.cocos2d-x.org/news/143
Unity 3D: Unity 3D是一个非常强大的商用跨平台开发工具,支持Windows Store, Windows Phone, iOS, Android, Wii等多个平台。Unity 3D是移动平台上3D引擎的领头羊,同时Unity 3D也能很好的支持2D图形开发。下载地址:
http://unity3d.com/unity/whats-new/unity-4.3
XNA:XNA可支持Windows, Xbox 360和Window Phone,以前使用XNA开发的针对Window Phone 7.x的游戏仍然可以在Windows Phone 8上运行。但不支持创建新的目标设备为WP8的游戏。请参考:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207003
(v=vs.105).aspx
http://msdn.microsoft.com/en-us/library/bb200104.aspx
MonoGame:MonoGame旨在帮助XNA开发者在Windows Phone 8, Windows 8,iOS, Android,Mac,Linux等多个平台实现跨平台开发。官方主页:
http://www.monogame.net/
在开源平台codeplex上的页面:http://monogame.codeplex.com/
Marmalade:Marmalade是一个非常有特色的跨平台游戏引擎,它支持在Windows上将应用Deploy至iOS。支持将OpenGL的程序直接转换成DirectX11的以移植到Windows Phone平台上。请参考:
https://www.madewithmarmalade.com/
http://www.madewithmarmalade.com/windows-phone-8
HTML5: 除了游戏引擎,还可以使用HTML5实现跨平台。对Windows Store游戏,可使用HTML5 和 JavaScript。如果熟悉HTML5 和 JavaScript (WinJS) 的开发,同时游戏不需要复杂的动画效果,并且对于游戏的实时性能没有非常高的要求,可以考虑这种游戏开发模式。
推荐使用的情景:充分利用HTML5 和 JavaScript(WinJS)的开发经验。 方便页游的迁移,缩短开发周期
不推荐使用的情景:如果你的游戏有大量的图像处理,或者对性能要求很高,使用HTML5 和 JavaScript 开发模式就不太适合你的游戏。 最好使用DirectX 和 C++ 结合。
请参考:http://msdn.microsoft.com/en-us/library/windows/apps/hh465158.aspx
开发例子:design.htmlwindowsapps/JavaScript-and-HTML5-touch-d96f6031
(2)针对不同平台分别用OpenGL / OpenGL ES或者DirectX实现渲染
在跨平台应用程序设计的时候,我们可以将渲染部分独立,最简单的设计是创建 Renderer类,分别针对Windows Phone, iOS和Android实现。众所周知,OpenGL ES主要用于Android和iOS平台的开发。而WP8和Windows Store使用DirectX11 API。接下来我们重点比较OpenGL和DirectX的异同,供开发者在切换平台时参考。
OpenGL ES / OpenGL与DirectX性能比较
这是一个在OpenGL和DirectX诞生以来很多人比较热衷于讨论的问题,所以在这里简要的提一下。其实一款显卡在设计的时候就会考虑要支持OpenGL和DirectX两种标准的所有feature,而不管是GLSL还是HLSL的shader,在经过驱动编译后都是在同样的硬件中同样的并行计算模块上运算,纹理等resource也是存储在同样的显存上。OpenGL和DirectX实现的应用程序其性能更多取决于驱动的优化程度而非OpenGL / DirectX API的优劣。OpenGL或者DirectX的API的选择对游戏的性能的影响主要局限于CPU部分而不是GPU。例如AMD最近发布的Mantle API其主要优化也是集中于减少CPU的开销,且仅适用于AMD显卡。而OpenGL和DirectX的API对CPU部分开销的比较至今没有一个定论,也即没有一方是明显优于另一方。OpenGL ES在设计时是参考OpenGL的,可以说是OpenGL的一个子集,其API也没有优于DirectX的说法。
开发者如果对游戏性能有所顾虑,可以更多的可考虑一些平台相关的特性而非API本身。例如在iOS使用pvr格式的纹理(PowerVR的有损压缩格式),而在Windows平台使用DDS格式的纹理。在下文中有关于手机纹理的详细说明:
手持平台纹理格式说明
硬件对OpenGL ES和DirectX的支持
在写这篇文档的时候(2013年10月),智能手机普遍可以支持OpenGL ES 2.0或以上了。下面的表格列出了OpenGL ES与DirectX的比较。OpenGL ES 2.0相当于DirectX 3D的feature level 9。OpenGL ES 2.0所支持的GLSL对应于DirectX的HLSL。
OpenGL ES版本列表
OpenGL ES版本号 对应OpenGL版本 对应的DX版本 版本特性
1.0 OpenGL 1.3 低于DX9 Fixed function rendering
1.1 OpenGL 1.5 低于DX9 Fixed function rendering。虽然OpenGL 1.5中引入了ARB shader,但是OpenGL ES 1.1中仍然是不支持的。
2.0 OpenGL 2.0 DX9 支持GLSL
3.0 OpenGL 3.0 DX10与DX9之间 兼容OpenGL ES 2.0,但不支持DX10中才有的Geometry Shader, OpenGL自3.2版本开始支持Geometry Shader.
OpenGL和OpenGL ES详细信息可参考Spec或Wiki中的介绍。可参考下面的链接:
http://www.khronos.org/opengles/
http://en.wikipedia.org/wiki/OpenGL
http://en.wikipedia.org/wiki/OpenGL
_ES
DirectX 版本(忽略DirectX9以前版本,因为不论WP还是Win8的硬件最低要求都是支持DX 9.1)
DX版本 与OpenGL ES对应关系 支持的 Shader
9.x 功能上相当于OpenGL ES 2.0 Vertex Shader, Pixel Shader
10.x 包含OpenGL ES 3.0的feature 增加了 Geometry Shader
11.x 包含DX10且包含OpenGL ES 3.0 增加了 Hull Shader和 Domain Shader
各应用平台对OpenGL ES、DirectX以及OpenGL(非ES)的支持
Android iOS Windows Phone / Windows Store
根据下面的讨论,在2010年,已经有超过75%的Android设备可以通过NDK支持OpenGL ES 2.0. 估计目前绝大多数Android设备均可使用至少OpenGL ES 2.0。
https://groups.google.com/forum/#
!topic/android-developers/HBddHFyxYeo
需要Android 4.3或以上才能支持OpenGL ES 3.0
从iPhone 3GS开始支持OpenGL ES 2.0.
从iPhone 5S开始支持OpenGL ES 3.0
从Windows Phone 8开始支持DirectX 11的API,其feature level是DX 9.3。开发必须使用DirectX 11的API。
Surface RT要求必须能运行DX 9.1的feature。
在Windows Store中针对x86和x64平台可使用DX10和DX11的feature。如无法支持DX9,需要在程序启动后显示说明。
在WP8之前的WP,可使用XNA开发,相当于DX9的feature level。
在Windows Store应用中,可使用OpenGL(非ES)开发。参考如下:
http://opengl.codeplex.com/
OpenGL ES 2.0是目前在iOS和Android平台上通常使用的图形解决方案,常用的游戏引擎中的渲染部分是对OpenGL ES的封装。OpenGL ES 2.0所对应的DirectX 版本是DirectX 9,。而WP8和Windows 8对硬件的最低硬件要求即DX9,这使得基于OpenGL或者游戏引擎的应用向WP/Windows 8移植变成很自然的事情。
WP8和Windows 8应用商店的应用需要使用DX11的API进行编程,我们只需要将DX11的Feature level设置为DX9即可(参考下面的代码片断)对应于OpenGL ES 2.0。对于Windows Store中的应用,我们还可以使用DX10和DX11中的feature, 实现比OpenGL ES 2.0更加强大的渲染功能和更好的性能。
D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; D3D_FEATURE_LEVEL APP_MIN_FEATURE_LEVEL = D3D_FEATURE_LEVEL_9_1;
如前所述,其实OpenGL ES 2.0是OpenGL 2.0的子集,对应于DirectX的Feature level 9所能够实现的功能是相当的。所以能够使用OpenGL实现的效果,通常也可以使用DirectX实现。Visual Studio支持两种DirectX的模板,一种是仅使用DirectX而不使用XAML,另外一种是将DirectX和XAML结合。我们比较推荐的是DirectX和XAML结合。因为XAML中定义的丰富的界面元素,交互的实现更加简单。第三方的SDK如微博,支付相关SDK都需要XAML的支持。在微软设计DirectX和XAML结合的时候,已经针对不同情景尽可能减少结合时对性能的影响。
XAML与DirectX结合使用的相关文档请参考:
http://channel9.msdn.com/Events/Windows-Camp/Developing-Windows-8-Metro-style-apps-in-Cpp/Building-Apps-with-Cpp-XAML-and-DirectX
http://blogs.msdn.com/b/windowsappdev/archive/2012/03/15/combining-xaml-and-directx.aspx
http://mobile.dzone.com/articles/directx-and-xaml-windows-phone
不管是OpenGL和DirectX,渲染时都需要维护所谓的Context。在Context中保存了一些渲染时的状态信息,例如是否使用了光照,是否启用了雾效,当前使用的纹理是什么等等。一般创建Context的过程都是由平台的模板自动生成,开发者只需要对Context进行适当的设置,并调用OpenGL或者DX的API进行渲染即可。OpenGL ES和DX11创建Context以渲染的过程分别如下(谨供参考,一般移动应用的程序员不需要关心细节):
最常见的渲染过程是设定vertex buffer(VB)、index buffer(IB)、纹理、shader、调用绘制函数,最后显示缓冲中的渲染图像。下面的表格将主要从这几个方面对OpenGL ES和DX作比较。
OpenGL ES DirectX 11
Vertex buffer 和 Index buffer(通常称VB和IB)
在显存/内存中的存储方式和二者没有区别,均按顺序线性存放数据。但使用VB和IB的API略不同
VB和IB中定义的模型通过draw函数绘制。
通常使用glDrawElements函数
偶尔使用glDrawArrays函数(不需要IB)
glBegin, glEnd, glVertex3f等OpenGL函数在OpenGL ES中因为效率的考虑已经移除。
Draw通常使用DrawIndexed绘制VB/IB中的内容
也有DrawInstanced, DrawAuto等函数可供选择
注意Instancing在DX9中已经支持,在OpenGL 3.0才支持。
在DX 10以后还可通过Geometry shader实现Instancing
VB中顶点的属性(Attribute) 如normal, color, texture坐标, position等等 使用glVertexAttribPointer函数分别指定顶点的各个属性
与DX的比较可参考下文:
将 OpenGL ES 2.0 缓冲区、uniform 和顶点属性与 Direct3D 进行比较
DX的API中没有Attribute这个概念,仅使用结构体 D3D11_INPUT_ELEMENT_DESC描述VB中每个顶点的数据格式
m_d3dDevice->CreateInputLayout
Shader / Program
OpenGL中Shader又叫Program
支持运行时编译,即GLSL文本在运行时编译。
分Vertex Program和Fragment Program
对于Windows Store和WP8需要在编译时将HLSL编译成生成伪汇编代码。如Shader编译出错则无法生成可执行文件。
在DX feature level 9中
分Vertext Shader和Pixel Shader分别对应于GLSL中的Vertex Program 和Fragment Program
Shader的常数设置 API中包含Uniform,如:
glGetUniformLocation
glUniform4fv
使用Constant Buffer相关函数,如:
m_d3dDevice->CreateBuffer
m_d3dContext->UpdateSubresource
m_d3dContext->VSSetConstantBuffers
资源管理,如纹理和buffer 使用glCreateShader, glBufferData, glGenBuffers, glTexImage2D, eglBindTexImage等等API管理资源 使用ID3D11Device管理资源。一般会有一个ID3D11Device指针,用以调资源管理的DX API ,如函数CreateTexture2D()。在Windows Store的应用可以使用一些已经封装好的Loader类加载纹理资源。微软的Sample中常使用BasicLoader类,程序员可在其基础上进一步修改增强。
Context管理, 用于配置渲染管线 glAttachShader, glGetUniform*等等API 一般创建ID3D11DeviceContext类的指针,再使用context指针调用Context配置的API
显示缓冲区内容 使用SwapChain函数 使用Present函数
坐标系
X正方向为右,Y正方向为上。
详细的异同可参考下面:
Coordinate Systems (Direct3D 9)
OpenGL使用右手坐标系,所以Z的正方向指向屏幕外。
DX使用左手坐标系。所以移植时三角形的顶点顺序要反向,Z的方向也要反向。
可以使用函数 D3DXMatrixPerspectiveRH 和D3DXMatrixOrthoRH来做右手坐标系的投影映射。