写一个支持SMB的手机VR播放器(二):Shader与建模

针对于上文中讲到的 Unity 端需求,首先需要验证的是拆分图像,使得左右眼显示不同内容的问题,而这就需要 Shader 的帮助。
为什么需要 Shader
CardBoard SDK 新版本接入了 UnityXR,而现代 UnityXR 采用SinglePassStereoRendering
渲染,也就是所谓的单通道立体渲染。
单通道立体渲染可以提高性能,但是这也就代表摄像机 Layer 分层,一只眼看一个面片的方式行不通了。所以我们需要写一个 Shader,使得同一个物体,左右眼渲染时显示不同的贴图。
我们使用下面这张图片来验证图像裁切,该图像是 VR 视频中取出的一帧画面。
建模 与 UV
先不谈 Shader,谈谈图像展示。
经常看 VR 视频的人一眼就能看出,上面那张图片是一张左右眼图像,左眼看左边,右眼看右边,每只眼的纹理宽高比为 1:1。
细心的你可能会发现图像边缘有弧形黑边,并且画面中的桌子是弯曲的,这是因为该纹理是一个 180 度半球弧面纹理。
当然,即使是半球面,因为两只眼睛看到的画面不同,因此人眼仍旧有立体感,这与全景视频是不同的。
打开 Blender,生成一个球体 Mesh:
用户在球体球心处观看内容,因此选择所有面,翻转法向,使所有面法线朝向球心:
对于上文中的视频来说,我们只需要半个球,因此删掉半个球的顶点:
删掉半球后,开始调整 UV,调整前,你的 UV 应该是这样的(本博删前半球,导致左右方向相反,即图中可见颜色顺序相反):
我们需要让图像从左到右填充球面,因此分 UV 时需要让各个面紧密排列,从左到右填满 Texture,同时注意 UV 平铺的顺序应是从左到右的,如果反向则需要校正:
最后,将模型绕 x 轴旋转 -90 度,应用变换,再绕 x 轴旋转 90 度,导出 FBX 到 Unity 即可。
Shader
对于SinglePassStereoRendering 也就是单通道立体渲染来说,场景内可以获取的只有一个摄像机。
为了让左右眼看到不同的图像,我们在 Shader 中需要知道当前渲染的是左眼还是右眼。Unity 考虑到了这种情况,提供了unity_StereoEyeIndex
参数标识当前渲染的眼睛。
我们的图像是横向裁切的,因此需要对 UV 的 x 进行修改:
//修改自 GoogleVR/Demos/VideoDemo StereoShader,添加了 uv.x 裁切、左右/右左判断
v2f vert (appdata_base v) {
v2f o;
o.pos = GvrUnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
o.uv.x *= 0.5f;
if(unity_StereoEyeIndex == _IsLeftRightNormal) { // _IsLeftRightNormal: 0 右左图像 1 左右图像
o.uv.x += 0.5f;
}
return o;
}
将本 Shader 指定到半球模型的材质上,并给此材质设置上面的贴图:
修改_IsLeftRightNormal
的值,可以在编辑器内预览左右眼的图像(因编辑器内 unity_StereoEyeIndex 为定值 0):
编辑器内将摄像机放到球心处,可以看到正常的图像:
播放视频
播放视频原理很简单,播放插件解码,之后将插件解码出的纹理赋到指定上文 Shader 的材质中,即完成左右/右左类型的视频显示。
其他
本文仅说明了左右/右左 180 度球面类型 VR 视频的显示,上下/下上类型的 VR 视频与之类似。
接下来需要写安卓端 Java 代码解决 SMB to HTTP 的问题。