BabylonJS系列:方向导航盒 2.创建导航盒场景

第一节完成了模型的自定义生成,第二节开始完成导航盒场景的制作,这里我使用多场景的方式制作导航盒,这种方式可以使导航盒场景和原场景解耦,避免产生各种各样的光照,深度,后处理等方面的冲突。首先进行场景的创建和初始化工作,这里会单独会创建一个Scene,Camera和DirectBox。这里和主场景一致创建了一个ArcRotateCamera,方便两个场景摄影机的同步,注意这个摄像机是正交摄像机,radius的大小对视角的没有影响,radius的正负会影响旋转。

class DirectBoxScene {
......
    constructor(engine: BABYLON.Engine, bindCamera: BABYLON.ArcRotateCamera) {
        this.engine = engine;
        //创建导航盒场景
        this.scene = new B.Scene(engine);
        //将场景的自动清除关闭,该场景将在主场景渲染结束后渲染,关闭autoClear避免将主场景渲染的图像清除。
        this.scene.autoClear = false;
        // //将场景的clearColor设置为透明
        // this.scene.clearColor.a = 0;
        this.scene.clearColor.set(0, 0, 0, 1);
        this.selfCamera = new B.ArcRotateCamera(
            "DTC", -Math.PI / 2, Math.PI / 2, 10, B.Vector3.Zero(), this.scene
        );

        this.selfCamera.mode = B.Camera.ORTHOGRAPHIC_CAMERA;
        this.selfCamera.orthoBottom = this.selfCamera.orthoLeft = -1
        this.selfCamera.orthoTop = this.selfCamera.orthoRight = 1;
        //Box
        const directBox = DirectBoxCreator.create(this.scene, 1, 0.3);
        this.directBox = directBox.mesh;
        //因为屏幕小导致edge过于细
        this.directBox.edgesWidth *= 2;
        this.directBox.material.zOffset *= 5;
        ......
    }
......
}

初始化过程中有一个重点关注的点,就是将副场景的autoClear设置为false,避免渲染副场景的时候将原场景的像素信息从Buffer里面清除掉。接下来是摄像机的同步和窗口的初始化,前者将副场景的摄像机与主场景的摄像机根据旋转方向进行同步,后者将设置副窗口在整个页面的位置,这里我以屏幕右上角为基准进行的计算:

class DirectBoxScene {
    //窗口大小  单位pixel
    windowSize: number = 200;
    //窗口右上角离window右上角的距离
    windowOffset: BABYLON.Vector2 = new BABYLON.Vector2(50, 50);
    ......
    constructor(engine: BABYLON.Engine, bindCamera: BABYLON.ArcRotateCamera) {
        ......
        this.alignCameraToBindCamera();
        this.resizeWindow();
        ......
    }
    ......
    //将摄像机与绑定摄像机同步
    alignCameraToBindCamera() {
        this.selfCamera.alpha = this.bindCamera.alpha;
        this.selfCamera.beta = this.bindCamera.beta;
        if (this.bindCamera.radius * this.selfCamera.radius < 0) {
            this.selfCamera.radius *= -1;
        }
    }
    //重置场景区域
    resizeWindow() {
        const engine = this.engine;
        const width = engine.getRenderWidth(true);
        const height = engine.getRenderHeight(true);

        this.selfCamera.viewport.x = 1 - (this.windowSize + this.windowOffset.x) / width;
        this.selfCamera.viewport.y = 1 - (this.windowSize + this.windowOffset.y) / height;

        this.selfCamera.viewport.width = this.windowSize / width;
        this.selfCamera.viewport.height = this.windowSize / height;
    }
}

这一步需要注意的是,ArcRotateCamera如果滚动滚轮使得radius为负,旋转的方向会发生反转,所以需要对正负进行同步,当然也可以直接设置radius的上下界,禁止radius为负值,另外一点就是viewport是以屏幕左下角为原点,它的x,y,width,height的范围均为0-1。最后是进行事件的绑定,一般建议写事件绑定的时候对应着写一个dispose函数,确保每一个事件都在销毁前注销掉,避免组件被销毁后,事件依旧触发导致报错崩溃。

class DirectBoxScene {
   
    ......
    //绑定摄像机旋转Observer
    _onCameraViewMatrixChangedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Camera>> = null;
    //场景尺寸改变Observer
    _onEngineResizeObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Engine>> = null;
    //绑定场景渲染Observer
    _onSceneRenderObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Scene>> = null;
    constructor(engine: BABYLON.Engine, bindCamera: BABYLON.ArcRotateCamera) {
        ......
        //绑定摄像机矩阵改变事件
        this._onCameraViewMatrixChangedObserver = this.bindCamera.onViewMatrixChangedObservable.add((camera) => {
            this.alignCameraToBindCamera();
        })
        //绑定窗口尺寸改变事件
        this._onEngineResizeObserver = this.engine.onResizeObservable.add((engine) => {
            this.resizeWindow();
        })
        const bindScene = bindCamera.getScene();
        //场景渲染事件
        this._onSceneRenderObserver = bindScene.onAfterRenderObservable.add((scene) => {
            this.scene.render();
        })
        ......
    }
    ......
    dispose() {
        this.bindCamera.onViewMatrixChangedObservable.remove(this._onCameraViewMatrixChangedObserver);
        this.engine.onResizeObservable.remove(this._onEngineResizeObserver);
        this.bindCamera.getScene().onAfterRenderObservable.remove(this._onSceneRenderObserver);

        this.scene.dispose();
    }
}

至此导航盒场景已经可以显现出来,之后将为导航盒添加新的功能。


PS:本节结束PG(https://playground.babylonjs.com/?#ENABP9#10)

下一节:BabylonJS系列:方向导航盒 3.添加点击归位事件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容