曹心怡 赵建军
(北京电影学院,北京 100088)
《阿凡达》以来,数字3D电影的发行给制片方带来了高额的票房,数字3D电影技术因此发展迅速。即使如此,3D电影制作过程中仍然要受到诸多因素的制约,导致拍摄时和成片后的效果不甚理想。Unity是当下使用最为广泛的3D引擎之一,其强大的功能为立体拍摄提供了得天独厚的支持,也为立体电影的虚拟制作提供了解决方案。
现代立体电影的发展过程中,拍摄画面从2K到4K,摄影机从双机平行放置到垂直90度立体支架放置,进而发展到便携式双镜头一体机。立体电影对于拍摄条件要求较高,且DI工艺中增加了立体调整环节。传统3D技术与Unity引擎和虚拟制作技术的结合,产生了像《狮子王》这种完全虚拟拍摄的立体电影。相比传统3D电影,在Unity引擎中进行虚拟拍摄拥有以下优点:
(1)传统3D电影受到摄影机和支架物理尺寸的制约,导致基线不能无限大小,拍摄一些特殊景别的镜头时非常受限。即使使用分光镜解决了双机光轴间距问题,也有很多局限性:损失2/3档光圈,缩小景深;工艺质量不够高造成反射面残影;系统结构复杂导致双机稳定性大打折扣等。Unity中的摄像机为虚拟摄像机,没有机身或支架尺寸的限制,基线理论上可以是无限大小。
(2)传统3D电影为常规的24帧格式,帧率不足造成的运动模糊会大大降低3D效果的品质。而Unity引擎中优化得当,帧率可以达到60帧甚至120帧。
(3)传统3D电影需要摄影师尽力追求更大的景深,以便增加左右两眼观看到两组画面的区分度和影像清晰度,以营造立体效果。Unity中的摄像机可以实现无限大景深,为立体虚拟空间提供足够清晰明亮的视觉线索。
(4)传统3D电影后期DI成本高,处理复杂。Unity封装的可编程渲染流水线能够让开发者更轻松地管理渲染设置,着色器(Shader)则能控制渲染细节,从而实现特定的渲染效果。
目前,利用Unity引擎创作立体影像的方法,有基于裸眼3D显示装置的显示系统,也有针对特定3D显示器的适配指南,立体效果优秀,但是普遍存在的问题就是成本较高、较难实施。
上述的两个Unity 立体影像方案都对硬件有较高的要求。而本文提出的方案则是利用渲染纹理(Render Texture)和Shader对左右摄像机输出的图像进行分别处理,实现方法成本低廉,实现简单,立体效果较好,拥有较强的可配置性。本方案的研究目标主要包括两部分:一是红青立体影像的基本功能实现,二是对于该方案的效果测试。
其中,红青立体影像的基本功能实现需要在Unity场景中建立摄像机,并把摄像机的渲染目标设置成渲染纹理。还需要了解红青立体影像的原理,并在Shader中实现左右机影像不同通道的分别渲染与合并工作。
对于该方案的效果测试需要完成零平面位置的调整对于立体效果的测试、摄像机横向运动对于立体效果的测试。
Unity 3D是Unity Technologies公司开发的一款3D引擎,因其强大的跨平台特性、绚丽的3D渲染效果以及自由丰富的人机交互功能而闻名出众。其跨平台开发特性可以节省开发时间,省去开发者在不同平台之间的移植开发工作。
新版《狮子王》就是使用Unity引擎进行虚拟制作,虽然现场有传统的电影设备,包括三脚架、dolly车、云台、调焦遥控器、摇臂、无人机,但是这些设备要么是被重新编程,像轨道和伸缩炮,用来确定摄像机的位置;要么是用Opti Track进行追踪,例如手持或斯坦尼康。真正用于拍摄的都是Unity内部的虚拟摄像机。最终实际使用的拍摄场景并不大,只有约21×12米,并且摄制组只用了其中大约1/3的空间就拍摄了《狮子王》的大部分内容。
使用Unity写Shader的一个好处在于,在Unity中,渲染管线被高度封装,用户不再需要亲力亲为手动计算一些值(通常是重复性的矩阵运算)。同时,它提供了很多内置的参数,能够让用户很方便地调用,实现特定的渲染效果。
人眼在观看自然物体时,两眼是从不同角度观看物体,从而在左右眼视网膜上形成稍有差异的像,然后再经过大脑分析融合形成立体视觉。3D拍摄时也是利用双目视差来形成立体感的。
本项目选择使用的是红青立体3D方法,通过红色和青色的色彩滤镜对每个眼睛的图像进行编码,当通过用颜色“编码”的彩色滤色片眼镜时,对应每只眼睛的图像都能够到达预期的眼睛,从而在大脑皮层中合成立体图像。
红青立体方案有以下优点:
(1)相比较滤色片更便宜的红蓝眼镜,红青眼镜能够更好地还原色彩,尤其是肤色。
(2)相比较偏振立体方案,不需要额外的显示设备,普通显示器即可观看,成本更加低廉。
(3)相比较平行视图(side-by-side)的立体图案,可以拥有更大的视场角,而不会造成观看者的不适。
基于以上方面,本项目使用红青立体3D方法作为使用Unity输出立体影像的方案。
着色器(Shader)是GPU渲染流水线中的一个环节,用来对三维物体进行着色处理、光影计算和纹理颜色呈现等。
图1是GPU的渲染流水线实现,颜色表示了不同阶段的可配置性或可编程性:绿色表示该流水线阶段是完全可编程控制的,黄色表示该流水线阶段可以配置但不是可编程的,蓝色表示该流水线阶段是由GPU固定实现的,开发者没有任何控制权。实线表示该Shader必须由开发者编程实现,虚线表示该Shader是可选的。
图1 GPU的渲染流水线实现
着色器中包括顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)两个部分,它们是GPU渲染流水线中完全可编程控制的部分。
在没有渲染纹理的时候,一个摄像机的渲染结果会输出到颜色缓冲中,并显示到屏幕上。现代的GPU允许我们把整个三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture,RTT),而不是传统的帧缓冲或后备缓冲(back buffer)。Unity为渲染目标纹理定义了一种专门的纹理类型——渲染纹理(Render Texture)。渲染纹理可以看成是一种特殊的纹理类型,它在引擎运行时可以实时更新。
针对得到的渲染纹理,本方案将利用Shader做如下处理:首先对输入的两张渲染纹理进行采样得到纹素值(Texel);然后把两张图片分别采样得到的纹素值,RGB通道分离处理;最后,取其中一张图片的红(R)通道和另一张图片的青(GB)通道,合成为一张新的纹理进行输出。
图2 方案结构图
本文使用的方案,首先在需要处理的Unity场景里面架设2台虚拟摄像机,再把两台摄像机的渲染目标设置成渲染纹理。把这两张渲染纹理作为输入,用Shader对它们分别做处理之后,输出到另一个材质,作为最终的输出。图2为方案的结构图。
本方案由两部分构成,一是Unity场景的搭建和前期工作的准备,二是在Shader中处理获得的渲染纹理,并输出最终效果。其中在Unity中的准备工作是红青影像方案实施的核心部分,下文将从3个角度详解本方案。
3.2.1 场景搭建
测试场景需要保证实验的正常进行,首先必须是三维场景,其次要有足够的景深,远、中、近景都有被摄物体分布。
最终确定的场景如图3。
图3 场景斜向俯瞰图
图4 Scene窗口摄像机景别
序号①处为远处的山峰,②为中景的石头,③为近景且在画面边缘的植物。最终确定的摄像机景别如图4。
3.2.2 摄像机搭建
在图4中确定的摄像机位置建立一个空物体,这个位置就是两台摄像机的中心所在的位置。把两台摄像机作为此空物体的子物体,位置、旋转全部归零。此时,这两台摄像机的视角均和图4中所呈现的一致。
为了模拟立体摄像机的双目视差,把两台摄像机分别向左右两边移动0.3单位,摄像机搭建完成。
目前两台摄像机均为平行拍摄,为了方便确定零平面的位置,还需要在Scene视图中显示出两台摄像机的主光轴。用两个x和y方向缩放均为0的正方体(Cube)分别作为左右两台摄像机的子物体,同样把位置、旋转全部归零,这样转动摄像机的时候这条两线也会跟着显示。用这种方法可以得到主光轴汇聚的位置。把主光轴汇聚的位置放在中景的石头上面。
图5 Scene窗口主光轴汇聚在中景的石头上
3.2.3 创建渲染纹理
在Project目录下创建一个渲染纹理,然后把左摄像机的渲染目标设置成该渲染纹理,这样一来该摄像机的渲染结果就会实时更新到渲染纹理中,而不会显示到屏幕上。使用这种方法,我们还可以选择渲染纹理的分辨率、滤波模式等纹理属性。右摄像机也做同样操作。
因为渲染纹理经过处理后最终要输出到屏幕上作为输出结果,因此大小要尽可能大。理想情况下,纹理可以是非正方形的,但是长宽的大小应该是2的幂。如果使用了非2的幂(Non Power of Two,NPOT)的纹理,那么这些纹理往往会占用更多的内存空间,GPU读取该纹理的速度也会有所下降。出于性能和空间的考虑,把纹理大小设置成1024×1024。
把两个渲染纹理分别赋给两个Quad,并排这两个Quad,用最终输出的摄像机拍摄side-by-side版本,测试双目立体效果。
图6 Game窗口Side-by-side测试输出
3.2.4 利用着色器(Shader)处理渲染纹理
着色器的可编程部分为顶点着色器和片元着色器。
在顶点着色器中并没有经过太复杂的处理。首先把顶点坐标从模型空间转换到剪裁空间中。虽然使用了两张纹理,但是它们不存在纹理坐标上的变化操作,所以它们共用同一组纹理坐标。
在片元着色器中,因为输出的是摄像机拍到的图像,因此不用对法线方向和光照方向进行处理,否则会影响到输出面片的光照效果。使用Cg的tex2D函数对输入的渲染纹理进行采样。它的第一个参数是需要被采样的纹理,第二个参数是一个float2类型的纹理坐标,它将返回计算得到的纹素值。
对两张渲染纹理做两次采样,其中左眼的纹理只输出纹素值的红色(R)通道,右眼的纹理只输出纹素值的青色(GB)通道。最后返回的颜色则是把它们混合在一起,形成新的彩色图像。关键代码如下:
fixed RedChannel=tex2D(_Left Tex,i.uv).r;
fixed2 CyanChannel=tex2D(_Right Tex,i.uv).gb;
return fixed4(RedChannel,CyanChannel,1.0);
分别调整左右摄像机的旋转参数,测试平行拍摄和汇聚在中景的石头上的效果。两者的输出如图7所示。
图7 左图:双机平行拍摄,右图:汇聚在中景的石头上
图7左图所有的物体都在负空间(即屏幕之前),很容易视觉疲劳。小恐龙左右像差过大,令观察者很难融像,努力融像之后视觉非常疲惫。在Photoshop中测量负像差为42像素,图片尺寸为1024像素,远远超过相差标准。
图7右图以中景为分界线,远景在正空间,近景的小恐龙和植物在负空间,虽然立体感觉不如左图强烈,但是没有任何不适。
将脚本Translating.cs拖拽给摄像机对,使两个摄像机能够一致往返不断运动。
调节摄像机对横向移动的速度,实验证明在帧率刷新极限范围内,速度增大都不会影响立体效果。因为Unity虚拟摄像机双机影像能够做到完全的帧同步,且摄像机画面是逐帧刷新的,没有果冻效应。
在上文中,提出了基于Unity游戏引擎的红青立体影像的实现方案,给出了此方案的研究内容和目标。然后详细介绍了Unity场景中准备工作应该如何进行,以及如何利用Shader对渲染纹理进行处理。最后简单测试了本方案的立体效果。
本方案还有许多工作需要进一步实施。首先是摄像机光轴可以使用gizmos.drawline函数在Scene视图中画出来。然后是利用屏幕后处理给屏幕影像加运动模糊,测试运动模糊对于影像的立体效果有无影响;给输出压一个暗角,改善边缘的显示效果。最后是测试镜面和透明物体对于立体空间的影响。