(中国传媒大学 北京 100024)
Unity是一款2005年在苹果的全球开发者大会上发布的游戏引擎,支持跨平台的2D/3D的开发,截止到2018年,该引擎已经支持了包含Windows、MacOS、任天堂的Switch、安卓、IOS等27个开发平台。它具备可视化的交互界面,让开发者不需要有非常强大的编程基础就可以使用,采用了层级化的开发环境,提供了非常详细的属性编辑器,并且可以在场景中实时动态的编辑和预览。它可以根据项目中的资源自动导入,并且支持资源的热更新,支持了大部分主流的第三方平台的建模格式、音频格式等素材。着色器语言使用了ShaderLab,并且提供了三种类型的着色器供开发者选择使用,让开发者可以根据需求灵活的处理。可以采用第三方提供的多人联网功能。内置了地形编辑器,能够非常容易的编辑出想要的地形[1]。基于这些特性,开发者们可以快速的制作出游戏原型或其他应用原型,因而被,被广泛用于到游戏、工业、建筑、医疗、教育等各个领域中。
但由于Unity引擎中原生的摄像机组件,在获取较大视场角的场景图像时会对图像产生较大的变形,这是因为Unity原生的摄像机采用了透视投影的方法,视场角较大时将对图像产生较大的变形。本文将应用方位等距投影的方式来获取场景,以消除图像的变形。
(一)Unity中的顶点变换
顶点变换的整体过程如图2-1所示。
首先从模型空间中的坐标开始,对于每个模型而言,它们的坐标在建模过程中就已被确定。模型空间到世界空间坐标需要使用变换矩阵,通过一定的缩放、旋转、平移操作,将获得最终的变换矩阵。世界空间将转成观察空间,需要注意的是Unity中的观察空间使用的是右手坐标系,所以需要将z轴进行反转。同样,也需要一个变换矩阵进行坐标的处理。观察空间到裁剪空间将要使用到投影模型,不同的投影模型将使用不同的变换矩阵。最后将裁剪空间变换到屏幕空间,需要用到齐次除法并获取到最终的像素位置[2]。
图 2-1 Unity中顶点变换过程
(二)着色器系统
着色器系统(shader)是一种对图像进行处理的计算机程序,可以进行颜色、光照、亮度等相关属性进行修改。一般着色器是利用图形处理器(Graphics Processing Unit,缩写为GPU)来进行处理[2]。Unity游戏引擎中包含三类着色器。
第一类是固定功能着色器(Fixed Function Shader),主要用于低版本显卡的兼容。第二类是表面着色器(Surface Shader),由Unity预制了很多光照模型,供开发者使用。第三类是顶点着色器和片段着色器(Vertex and Fragment Shader)顶点着色器(Vertex Shader)是处理三角面片的顶点,为最终渲染像素做铺垫的程序
片段着色器(Fragment Shader)又叫像素着色器(Pixel Shader),通过计算”片段”(单独的像素)的光照、亮度等属性。片段着色器的输出可以只有颜色,也可以附加一些例如高光、透明效果等。由于片段着色器可以获取屏幕的坐标,因而可以将纹理作为输入,生成更高级的后期特效[3]。
(三)投影
投影是对平面中的点和三维立体空间中的点进行相互转化的一种映射,通过利用数学方法来将其进行精确的转换。如果投影的表面是一个不能够完全战平的曲面,那么转换过程将带来变形进而造成误差。但可以根据使用需求,去缩小相应的误差,选择使用不同的投影模型来进行不同的数学方法,可以得到不同的最终结果。
由于分类标准的差异,投影模型分为不同的类别:
1.通过构成方法,投影模型包含了(1)几何投影。几何投影包含了三种投影模型,分别分为方位投影、圆柱投影、圆锥投影等。(2)非几何投影。
2.通过变形性质,投影模型包含了(1)等积投影;(2)等角投影;(3)任意投影
几何投影模型:几何投影是将立体空间投影到辅助的投影平面上,再通过展开来变成平面的一种投影方法。几何投影方法的特点在于,其将立体空间投影到圆锥或者圆柱上,通过将其展开,可以得到投影平面。根据不同的几何模型,分为了不同的投影模型,比如圆柱、圆锥、方位等投影。
方位投影模型:方位投影在投影屏幕上由投影中心向各个方向的方位角与真实空间中的相等。可以分为两大类,分别为透视方位投影和非透视方位投影。透视方位投影由于视点位置的不同,会得到不同的投影,比如正射、球心、球面等投影。而非透视方位投影可以分为三大类,是根据变形性质的不同,有等积、等角、等距投影等。方位投影对于圆形区域的平面,更具优势。
方位等距投影由德国制图家麦卡托提出,通过将球体与投影的平面相切,等距离的将经纬线投影在平面上。方位等距投影最显著的特点是在投影平面上到任意一点的距离都是真实的,所以在投影平面中的圆与三维空间中的真实地点是等距离的。不仅如此,从中心出发的方向也是与三维空间相同的[3]。
本文将使用的投影就是方位等距投影模型。
本设计采用结合方位等距投影模型,使用Unity中的脚本和着色器来构建一个大视场相机。场景的获取主要由五个C#脚本和两个着色器脚本完成,分别为CameraProjector、CPGrabber、CPMasks、CPPartial、CPSetup、CPUtility、ProjecterIncludes、ProjecterShaderCubic。最终通过将CameraProjector脚本挂靠到摄像机上进行调用。
程序实现的主函数梳理如下:
1.首先调用Reset函数中将首先判断当前场景面向是左眼、右眼或者非立体环境。之后也会根据相应的情况,调用类CPSetup中的Setup函数完成对于相应相机的初始位置。
2.然后对当前环境进行检测,查看是否支持RenderToCubemap这个API,如果支持则进行下一步,并调用reset函数,防止脚本并非第一次调用而未进行初始化;如果不支持否则发出提示该设备并不支持。
3.然后调用OnPreCull函数,该函数在相机剔除场景之前会被触发。相机可见的对象取决于剔除。首先调用了GrabTextures函数,然后利用一些变量保存了摄像机的cullingMask;、clearFlags、depthTextureMode等值,并对当前这些值进行清空,为的是在对图像处理完之后的还原。
4.其中GrabTextures函数将根据赋予脚本的参数计算出后面将要调用的RenderToCubemap的参数中的mask,mask是一个二进制数字,用于确定渲染六个面中的哪些面。并根据当前是否开启了VR模式,对左右眼分别调用了类Grabber中的Render函数。如果计算过程中出现错误信息,则将调用reset函数将设置还原。
5.Render函数将首先调用类CameraProjector中的ValidateRenderTexture函数对纹理进行修改,然后调用RenderToCubemap函数将,摄像机渲染的图像渲染到该纹理上。
6.ValidateRenderTexture函数将设定纹理的类型设置、分辨率、纹理的过滤模式、抗锯齿模式,然后创建一个新的纹理。
7.RenderToCubemap函数将利用之前 GrabTextures函数计算出的mask值来创建一份相应场景的立方体纹理。
8.至此OnPreull函数调用完毕。开始调用OnRenderImage函数,首先判断是否设置了背景,然后根据是否开启了VR模式,而对类CPGrabber中的Blit函数进行调用。
9.在Blit函数中,首先调用类CameraProjector中的ValidateMaterial函数,然后调用Graphics.Blit。
10.ValidateMaterial函数将新建一个材质,并由类CPUtility中的shaderblitShaderCubic来进行处理,并对其调用类CPUtility中的函数对材质进行属性的修改。
11.shaderblitShaderCubic是CameraProjecterShaderCubic文件中定义的shader。该shader在应用了方位等距投影模型。
12.最后回到Blit函数中,调用Graphics.Blit,以之前生成的纹理和处理过的材质为参数,将最终的图像渲染到屏幕上。
其中RenderToCubemap函数是Unity中一个自带的内部函数,该函数首先获取到当前摄像机拍摄的画面,并把画面渲染到指定的贴图上。该函数可以传入两个参数,分别为渲染目标的贴图和渲染的面数。
核心步骤的伪代码如下:
新建贴图纹理:
Texture newTex;
利用RenderToCubemap函数获取到相机位置的贴图:
RenderToCubemap(newTex)
应用方位等距投影:
half3 mapEquidistant(half2 uv)
{
half c=length(uv);
return half3(uv.x*sin(c),-uv.y*sin(c),c*cos(c));
};
(一)使用过程与实现效果
将该设计赋予到场景中的主摄像机上,并通过设置不同的视场角来获取不同的图像。将设计设置完毕之后,通过将摄像机置于一个包含六个相同面的盒子中,进行最终效果的对比,如图4-1所示,是使用了Unity原生的摄像机获取的场景图像。图4-2是使用了本设计,获取到了更大视场的场景图像。
图 4-1 Unity原摄像机
图 4-2 使用了本设计的摄像机
(二)性能分析
通过衡量该设计在场景中每秒的帧率,来评估该设计的运行效率。实验的测试环境的硬件信息为:处理器是I7-6700HQ@2.60GHz,显示适配器是NVIDIA GeForce GTX 950M。分别将设计放于含有七十万面的场景、六十万面的场景、五十万面的场景、四十万面的场景、三十万面的场景、二十万面的场景、十万面的场景、一千面的场景。测试结果如表4.1和图4-4所示。
表4.1 不同面数中的帧率的对比
图 4-4 帧率对比图
可以发现,该设计在不同面数的场景中的帧率,虽然低于没有使用该设计的场景,但帧率降低的较小,并且属于在人眼较为舒适的范围内,因而可以接受。
本设计是结合了方位等距投影模型实现的一个Unity的插件,使用了脚本技术和着色器技术来构建可以获得更大视场角的摄像机。然而由于需要对于最终渲染结果进行处理,所以需要进行较为繁琐的计算,性能上会有一定的开销,而本文未对性能方面加以优化,未来可以在性能的优化上进行进一步的探索。