网页3D之babylon.js

最近公司有虚拟仿真方面的想法,所以做了一些网页3D方面的研究,以及代码,这里记录一下

框架选择

之前用过threejs,所以一开始考虑用threejs做,但是之前用threejs的时候碰到过一个问题:物体位置坐标以及角度的调整太麻烦。每次调整后要刷新网页看下调整效果,再根据效果修改数据,反复修改。一个模型有时候就能弄一下午,实在是浪费了太多生命在上面。
后来在github上看到了babylonjs,看到官网上炫酷的例子效果,就被吸引住了。更吸引我的是babylonEditor,网页上的三维建模可以在这个编辑器里面做,简单的点击拖拽就可以完成大部分工作。后面再对物体写一写脚本。一个简单的模型就被开发出来了。
那就没什么好说的了,初步定,用babylonjs开始做。

开发过程

babylonjsbabylonEdtior都可以在github上找到,我这里是实现了一个简单的demo,效果如图:
网页3D之babylon.js_第1张图片

说一下这里面包含的功能点:
前面五点都是可以通过babylonEdtior做的,后面四点需要写脚本,脚本语言是typescript
网页3D之babylon.js_第2张图片

  1. 简单模型的创建,摆放
    中间是预览区域,可以拖动鼠标旋转,或者进行一些操作。点击菜单栏的add,addmesh,可以往预览中添加形状,相机,灯光,天空等,选择对应的对象,可以点击preview下面的那三个按钮,进行旋转,拖拽,缩放。或者在左侧边框直接键入对应的值。仅仅这一个功能我就感觉太好用了,至少不用来回调试了。

  2. 模型的皮肤贴图
    选择对象后,可以在左侧的操作框中选择皮肤material,然后给material贴图texture,贴图一般用图片就行,比如我截图中的方块就是这么做的。很人性化,开发起来真爽。

  3. 导入外部三维模型
    这个就更简单了,把三维文件拖到下面这个位置,再拖到页面中,结束。注意下面这个assets是静态文件,从上到下:网格、皮肤、贴图、音频,脚本。网页3D之babylon.js_第3张图片

  4. 天空盒的搭建,可以搭建任何场景
    天空盒的搭建有一点不一样,贴图不能使用简单的图片,点击Texture→add→pur cube Textures,看到下面这个框,分别选择六边形的六个面的图片,这时将天空或者场景沙盒的六个面的贴图选择进去,点击create就ok了。网页3D之babylon.js_第4张图片
    然后新建一个皮肤,空的就可以,再将这个皮肤的Reflection贴图选择刚刚新建的贴图,就可以得到一个沙盒的场景。
    到此为止,我们还没有写一行代码,点击play,已经在网页端运行起了一个三维的场景,场景中包含了一个物体。

  5. 物理引擎,碰撞检测
    为了使相机和物体有碰撞检测和重力,可以模拟现实中的情况,我们开始使用物理引擎。非常简单,给场景添加重力,并且允许检测碰撞。再给相机,物体都勾上物理属性,简单的物理引擎就实现了。注意,地面的重力设为0.
    网页3D之babylon.js_第5张图片
    网页3D之babylon.js_第6张图片

  6. 脚本创建
    新建脚本,并且指定给某个对象,双击可以打开脚本,编写具体内容。
    脚本中包含,onInitialize()创建的钩子函数,这个函数里还没有this,onStart()开始的钩子函数,开始时调用,onUpdate()每次浏览器页面刷新时调用,就是浏览器的每一帧都会调用这个函数。比使用定时器效率高,更稳定。
    我们可以在onStart中写一写初始化的操作,比如,创建GUI、创建Mesh,事件注册等。可以在update中写一写物体的位移,旋转等。
    比如,物体在每次网页页面更新时旋转一度。

    public onUpdate(): void {
      this.rotate(Axis.Y, Math.PI/180)
    }

网页3D之babylon.js_第7张图片

  1. babylon GUI
    babylon本身有一套GUI比如按钮,文本,slider,checkbox等。
    下面这个是官方的实例。
    网页3D之babylon.js_第8张图片

js

var createScene = function () {
    var scene = new BABYLON.Scene(engine);
    var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, 1, 110, BABYLON.Vector3.Zero(), scene);
    camera.attachControl(canvas, true);
    var hemi = new BABYLON.HemisphericLight("toto");
    var sphereMaterial = new BABYLON.StandardMaterial;
    var sphere1 = BABYLON.Mesh.CreateSphere("Sphere1gdtdyf", 1, 9, scene);
    var sphere2 = BABYLON.Mesh.CreateSphere("Sphere2", 2, 9, scene);
    var sphere3 = BABYLON.Mesh.CreateSphere("Sphere3", 3, 9, scene);
    var sphere4 = BABYLON.Mesh.CreateSphere("Sphere4", 10, .5, scene);
    var sphere5 = BABYLON.Mesh.CreateSphere("Sphere5", 4, 9, scene);
    var sphere6 = BABYLON.Mesh.CreateSphere("Sphere6", 10, 9, scene);
    var sphere7 = BABYLON.Mesh.CreateSphere("Sphere7", 100, 9, scene);
    sphere1.position.x = -30;
    sphere2.position.x = -20;
    sphere3.position.x = -10;
    sphere4.position.x = 0;
    sphere5.position.x = 10;
    sphere6.position.x = 20;
    sphere7.position.x = 30;
    sphere1.material = sphereMaterial;
    sphere2.material = sphereMaterial;
    sphere3.material = sphereMaterial;
    sphere4.material = sphereMaterial;
    sphere5.material = sphereMaterial;
    sphere6.material = sphereMaterial;
    sphere7.material = sphereMaterial;
    var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1");
    var panel = new BABYLON.GUI.StackPanel;
    panel.width = .25;
    panel.rotation = .2;
    panel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    advancedTexture.addControl(panel);
    var button1 = BABYLON.GUI.Button.CreateSimpleButton("but1", "Click Me");
    button1.width = .2;
    button1.height = "40px";
    button1.color = "white";
    button1.cornerRadius = 20;
    button1.background = "green";
    button1.onPointerUpObservable.add(function () {
        circle.scaleX += .1
    });
    panel.addControl(button1);
    var circle = new BABYLON.GUI.Ellipse;
    circle.width = "50px";
    circle.color = "white";
    circle.thickness = 5;
    circle.height = "50px";
    circle.paddingTop = "2px";
    circle.paddingBottom = "2px";
    panel.addControl(circle);
    var button2 = BABYLON.GUI.Button.CreateSimpleButton("but2", "Click Me 2");
    button2.width = .2;
    button2.height = "40px";
    button2.color = "white";
    button2.background = "green";
    button2.onPointerUpObservable.add(function () {
        circle.scaleX -= .1
    });
    panel.addControl(button2);
    var createLabel = function (mesh) {
        var label = new BABYLON.GUI.Rectangle("label for " + mesh.name);
        label.background = "black";
        label.height = "30px";
        label.alpha = .5;
        label.width = "100px";
        label.cornerRadius = 20;
        label.thickness = 1;
        label.linkOffsetY = 30;
        advancedTexture.addControl(label);
        label.linkWithMesh(mesh);
        var text1 = new BABYLON.GUI.TextBlock;
        text1.text = mesh.name;
        text1.color = "white";
        label.addControl(text1)
    };
    createLabel(sphere1);
    createLabel(sphere2);
    createLabel(sphere3);
    createLabel(sphere4);
    createLabel(sphere5);
    createLabel(sphere6);
    var label = new BABYLON.GUI.Rectangle("label for " + sphere7.name);
    label.background = "black";
    label.height = "30px";
    label.alpha = .5;
    label.width = "100px";
    label.cornerRadius = 20;
    label.thickness = 1;
    label.linkOffsetY = 30;
    label.top = "10%";
    label.zIndex = 5;
    label.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
    advancedTexture.addControl(label);
    var text1 = new BABYLON.GUI.TextBlock;
    text1.text = sphere7.name;
    text1.color = "white";
    label.addControl(text1);
    var line = new BABYLON.GUI.Line;
    line.alpha = .5;
    line.lineWidth = 5;
    line.dash = [5, 10];
    advancedTexture.addControl(line);
    line.linkWithMesh(sphere7);
    line.connectedControl = label;
    var endRound = new BABYLON.GUI.Ellipse;
    endRound.width = "10px";
    endRound.background = "black";
    endRound.height = "10px";
    endRound.color = "white";
    advancedTexture.addControl(endRound);
    endRound.linkWithMesh(sphere7);
    var plane = BABYLON.Mesh.CreatePlane("plane", 20);
    plane.parent = sphere4;
    plane.position.y = -10;
    var advancedTexture2 = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(plane);
    var panel2 = new BABYLON.GUI.StackPanel;
    panel2.top = "100px";
    advancedTexture2.addControl(panel2);
    var button1 = BABYLON.GUI.Button.CreateSimpleButton("but1", "Click Me");
    button1.width = 1;
    button1.height = "100px";
    button1.color = "white";
    button1.fontSize = 50;
    button1.background = "green";
    panel2.addControl(button1);
    var textblock = new BABYLON.GUI.TextBlock;
    textblock.height = "150px";
    textblock.fontSize = 100;
    textblock.text = "please pick an option:";
    panel2.addControl(textblock);
    var addRadio = function (text, parent) {
        var button = new BABYLON.GUI.RadioButton;
        button.width = "40px";
        button.height = "40px";
        button.color = "white";
        button.background = "green";
        button.onIsCheckedChangedObservable.add(function (state) {
            if (state) {
                textblock.text = "You selected " + text
            }
        });
        var header = BABYLON.GUI.Control.AddHeader(button, text, "400px", {isHorizontal: true, controlFirst: true});
        header.height = "100px";
        header.children[1].fontSize = 80;
        header.children[1].onPointerDownObservable.add(function () {
            button.isChecked = !button.isChecked
        });
        parent.addControl(header)
    };
    addRadio("option 1", panel2);
    addRadio("option 2", panel2);
    addRadio("option 3", panel2);
    addRadio("option 4", panel2);
    addRadio("option 5", panel2);
    scene.registerBeforeRender(function () {
        panel.rotation += .01
    });
    var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
    advancedTexture.layer.layerMask = 2;
    var panel3 = new BABYLON.GUI.StackPanel;
    panel3.width = "220px";
    panel3.fontSize = "14px";
    panel3.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
    panel3.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER;
    advancedTexture.addControl(panel3);
    var checkbox = new BABYLON.GUI.Checkbox;
    checkbox.width = "20px";
    checkbox.height = "20px";
    checkbox.isChecked = true;
    checkbox.color = "green";
    var panelForCheckbox = BABYLON.GUI.Control.AddHeader(checkbox, "checkbox", "180px", {
        isHorizontal: true,
        controlFirst: true
    });
    panelForCheckbox.color = "white";
    panelForCheckbox.height = "20px";
    panelForCheckbox.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    panel3.addControl(panelForCheckbox);
    var header = new BABYLON.GUI.TextBlock;
    header.text = "Slider:";
    header.height = "40px";
    header.color = "white";
    header.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    header.paddingTop = "10px";
    panel3.addControl(header);
    var slider = new BABYLON.GUI.Slider;
    slider.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    slider.minimum = 0;
    slider.maximum = 2 * Math.PI;
    slider.color = "green";
    slider.value = 0;
    slider.height = "20px";
    slider.width = "200px";
    slider.onValueChangedObservable.add(function(val){
        console.log(val)
    })
    panel3.addControl(slider);
    header = new BABYLON.GUI.TextBlock;
    header.text = "Sphere diffuse:";
    header.height = "40px";
    header.color = "white";
    header.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    header.paddingTop = "10px";
    panel3.addControl(header);
    var picker = new BABYLON.GUI.ColorPicker;
    picker.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    picker.value = sphereMaterial.diffuseColor;
    picker.height = "150px";
    picker.width = "150px";
    picker.onValueChangedObservable.add(function (value) {
        sphereMaterial.diffuseColor = value
    });
    panel3.addControl(picker);
    return scene
};
var demo = {
    constructor: createScene, onload: function () {
    }
};

html




    
    Babylon.js - GUI demo
    
    
    
    
    
    
    


    
    

Control panel
Active camera:

Change control method:

Post-processes:

  1. 点击事件
var cubeMesh = this.getScene().getMeshByName("Cube1")
        cubeMesh.actionManager = new ActionManager(cubeMesh.getScene())
        cubeMesh.actionManager.registerAction(
            new ExecuteCodeAction({
                trigger: ActionManager.OnLeftPickTrigger,
            },
                function (e) {
                    window["clickCube1"]()
                    // console.log(12313)
                    // location.href = "https://www.baidu.com/?tn=62095104_31_oem_dg"
                })
        )
        cubeMesh.actionManager.registerAction(
            new ExecuteCodeAction({
                trigger: ActionManager.OnLongPressTrigger,
            },
                function (e) {
                    console.log(3333)
                    // location.href = "https://www.baidu.com/?tn=62095104_31_oem_dg"
                })
        )
  1. 动画
 var addAnimation = function(mesh) {
            var animationBox = new Animation(
                "myAnimation",
                "rotation.x",
                30,
                Animation.ANIMATIONTYPE_FLOAT,
                Animation.ANIMATIONLOOPMODE_CYCLE
             );
             var keys = []
             keys.push({
                 frame: 0,
                 value: 0
             })
             keys.push({
                frame: 100,
                value: Math.PI
            })
            animationBox.setKeys(keys)
            mesh.animations = []
            mesh.animations.push(animationBox)
            return scene.beginAnimation(mesh, 0, 100, true)
        }

你可能感兴趣的:(前端,javascript)