HTC VIVE开发教程(二)

这一节我介绍的主要内容有

  • SteamVR渲染机制

用Unity做游戏开发的核心之一就是图形渲染,做VR开发当然也是一样,在这一节,我们就来看看SteamVR的图形渲染原理。SteamVR_Render.cs是SteamVR图形渲染的核心,该类位于Scripts文件夹中。接下来我来详细分析一下这个类。

下面是该脚本的OnEnable()方法

    void OnEnable()
    {
        StartCoroutine("RenderLoop");   //启动渲染
        SteamVR_Utils.Event.Listen("input_focus", OnInputFocus); //输入监听
        SteamVR_Utils.Event.Listen("Quit", OnQuit);  //退出监听
        SteamVR_Utils.Event.Listen("RequestScreenshot", OnRequestScreenshot); //截屏监听

        var vr = SteamVR.instance;   //获取SteamVR实例,用来判断头显设备是否安装好
        if (vr == null)
        {
            enabled = false;
            return;
        }

        /*
        public enum EVRScreenshotType
        {
            None = 0,
            Mono = 1,
            Stereo = 2,
            Cubemap = 3,
            MonoPanorama = 4,
            StereoPanorama = 5,
        }
        EVRScreenshotType.StereoPanorama为默认的截屏类型
        */
        var types = new EVRScreenshotType[] { EVRScreenshotType.StereoPanorama};

        OpenVR.Screenshots.HookScreenshot(types);  //初始化截屏,设置截屏类型
    }

可以看到里面最主要的方法是StartCoroutine(“RenderLoop”),它启动了渲染循环,我们再来分析这个方法

    private IEnumerator RenderLoop()
    {
        while (true)                                    //死循环用来不断的进行渲染
        {
            yield return new WaitForEndOfFrame();      //等待所有相机和GUI都渲染完

            if (pauseRendering)                        //渲染暂停,用来实现VR运行时暂停的需求
                continue;

            var compositor = OpenVR.Compositor;   //获取合成器,合成器的作用用来简化图像显示,是一个核心类
            if (compositor != null)
            {
                if (!compositor.CanRenderScene())
                    continue;

                compositor.SetTrackingSpace(trackingSpace); //设置跟踪控件类型,默认为站姿

/*这段if end的代码主要Unity5.0及以上版本中,通过GetLastPoses()不断的获取设备位置,然后通过SteamVR_Utils.Event.Send方法发送通知,头显和手柄中的监听器这个事件,从而不断的显示界面中更新位置*/
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
                SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_WaitGetPoses);

                SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - Begin");
                SteamVR_Camera.GetSceneTexture(cameras[0].GetComponent<Camera>().hdr).GetNativeTexturePtr();
                SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - End");

                compositor.GetLastPoses(poses, gamePoses);
                SteamVR_Utils.Event.Send("new_poses", poses);
                SteamVR_Utils.Event.Send("new_poses_applied");
#endif
            }

            var overlay = SteamVR_Overlay.instance;
            if (overlay != null)
                overlay.UpdateOverlay();              //更新overlay

            RenderExternalCamera();                  //渲染外部相机

#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
            var vr = SteamVR.instance;
            RenderEye(vr, EVREye.Eye_Left);        //开始渲染左右眼
            RenderEye(vr, EVREye.Eye_Right);

            // Move cameras back to head position so they can be tracked reliably
            //还原相机位置,在上面渲染左右时对相机位置进行了调整,所以这儿要还原
            foreach (var c in cameras)
            {
                c.transform.localPosition = Vector3.zero;
                c.transform.localRotation = Quaternion.identity;
            }

            if (cameraMask != null)
                cameraMask.Clear();
#endif
        }
    }

总结一下这个方法里面的渲染流程①等待相机和GUI的渲染完成->②设置跟踪空间->③获取设备位置,通知更新->④渲染外部相机->⑤渲染左右眼

这里写图片描述

看完了RenderLoop中的循环,我们再来看看左右眼画面的渲染,也就是我们在VR头盔中看到的画面。

    void RenderEye(SteamVR vr, EVREye eye)
    {
        int i = (int)eye;
        SteamVR_Render.eye = eye;

        if (cameraMask != null)
            cameraMask.Set(vr, eye);

        foreach (var c in cameras)
        {
            /*左右眼的视差使我们看到的世界有了立体的效果,VR的成像原理也是这样,左右两块镜片显示的画面是有些微差别的,所以这儿对左右眼中的画面进行了位置的修改*/
            c.transform.localPosition = vr.eyes[i].pos;
            c.transform.localRotation = vr.eyes[i].rot;

            // Update position to keep from getting culled
            cameraMask.transform.position = c.transform.position;

            var camera = c.GetComponent<Camera>();
            //将SteamVR_Camera中的纹理设置为实际渲染相机的纹理
            camera.targetTexture = SteamVR_Camera.GetSceneTexture(camera.hdr);
            int cullingMask = camera.cullingMask;
            if (eye == EVREye.Eye_Left)
            {
                //渲染左眼时把右眼特有的mask去掉,加上左眼特有的mask
                camera.cullingMask &= ~rightMask;
                camera.cullingMask |= leftMask;
            }
            else
            {
                //渲染右眼时把左眼特有的mask去掉,加上右眼特有的mask
                camera.cullingMask &= ~leftMask;
                camera.cullingMask |= rightMask;
            }
            //手动调用相机的Render方法
            camera.Render();
            camera.cullingMask = cullingMask;
        }
    }

到这里,我们就知道原来VIVE是将一个相机上的图像变形后分别显示再左右眼的显示屏上的。也知道了SteamVR的渲染流程。所以我们也就可以实现自由绘制左右眼的显示内容,控制VR渲染流程这些功能了。

猜你喜欢

转载自blog.csdn.net/tyuiof/article/details/52527560