网页3D之babylon.js

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

框架选择

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

开发过程

babylonjsbabylonEdtior都可以在github上找到,我这里是实现了一个简单的demo,效果如图:
在这里插入图片描述

说一下这里面包含的功能点:
前面五点都是可以通过babylonEdtior做的,后面四点需要写脚本,脚本语言是typescript
在这里插入图片描述

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

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

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

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

  5. 物理引擎,碰撞检测
    为了使相机和物体有碰撞检测和重力,可以模拟现实中的情况,我们开始使用物理引擎。非常简单,给场景添加重力,并且允许检测碰撞。再给相机,物体都勾上物理属性,简单的物理引擎就实现了。注意,地面的重力设为0.
    在这里插入图片描述
    在这里插入图片描述

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

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

在这里插入图片描述

  1. babylon GUI
    babylon本身有一套GUI比如按钮,文本,slider,checkbox等。
    下面这个是官方的实例。
    在这里插入图片描述

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

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width">
    <title>Babylon.js - GUI demo</title>
    <!-- <script src="//cdn.webglstats.com/stat.js" defer="defer" async="async"></script> -->
    <!-- <script>
        (function (i, s, o, g, r, a, m) {
            i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
                (i[r].q = i[r].q || []).push(arguments)
            }, i[r].l = 1 * new Date(); a = s.createElement(o),
            m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
        })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

        ga('create', 'UA-41767310-1', 'babylonjs.com');
        ga('send', 'pageview');

    </script> -->
    <link rel='stylesheet' href='https://d33wubrfki0l68.cloudfront.net/css/00b7b31c511ce943616fd1c5512819b8fcf13ab6/css/index.css'/>
    <!-- <script src="https://code.jquery.com/pep/0.4.0/pep.min.js"></script> -->
    <script src="babylon.js"></script>
    <script src="babylon.gui.min.js"></script>
    <script src='a123.js'></script>
</head>
<body>
    <canvas id="renderCanvas" touch-action="none"></canvas>
    <div id="fps"></div>
    <div id="stats"></div>
    <div id="status"></div>
    <div id="controlPanel">
        <div id="controlsZone">
            <p>
                <button id="enableDebug">Debug layer</button>
            </p>
            <p>
                <button id="fullscreen">Fullscreen</button>
            </p>
        </div>
        <div class="tag">Control panel</div>
        <div class="tag" id="clickableTag"></div>
    </div>
    <div id="cameraPanel">
        <div id="cameraControlsZone">
            Active camera:<br>
            <select id="camerasList"></select>
            <p>
                Change control method:
                <button class="buttonControlPanel" id="touchCamera">
                    <img src="https://d33wubrfki0l68.cloudfront.net/dcd183b9dc6674b3acb29895c9be9c241e933435/21aa7/assets/camtouchoff.png" class="buttonImg" />Touch camera
                </button>
                <button class="buttonControlPanel" id="deviceOrientationCamera">
                    <img src="https://d33wubrfki0l68.cloudfront.net/b79ca59bdd1cb71dd762d9a703fd40cf1ab0cb2e/e70d2/assets/camdeviceoff.png" class="buttonImg" />VR Device orientation camera
                </button>
                <button class="buttonControlPanel" id="gamepadCamera">
                    <img src="https://d33wubrfki0l68.cloudfront.net/39496e30a5783bade6a71f853eda7c4e11fff147/09f04/assets/camgamepadjoy.png" class="buttonImg" />Gamepad camera
                </button>
                <button class="buttonControlPanel" id="virtualJoysticksCamera">
                    <img src="https://d33wubrfki0l68.cloudfront.net/91e4f8a450087a60aec5c3cc9bb8b3cbdb52d341/a5f13/assets/camvirtualjoy.png" class="buttonImg" />Virtual joysticks camera
                </button>
                <button class="buttonControlPanel" id="anaglyphCamera">
                    <img src="https://d33wubrfki0l68.cloudfront.net/3a29c33554bd3b3723badb1507c9de8e1859064c/b0895/assets/camanagly.png" class="buttonImg" />Anaglyph camera
                </button>
            </p>
            <p>
                Post-processes:
                <button class="smallButtonControlPanel" id="toggleFxaa">Toggle FXAA (antialiasing)</button>
                <button class="smallButtonControlPanel" id="toggleFsaa4">Toggle FSAA 4X (antialiasing)</button>
                <button class="smallButtonControlPanel" id="toggleBandW">Toggle Black and white</button>
                <button class="smallButtonControlPanel" id="toggleSepia">Toggle Sepia</button>
            </p>
        </div>
        <div class="cameraTag"><img src="https://d33wubrfki0l68.cloudfront.net/4be4d643f634ae7cb26ddff105477a067c434512/11f81/assets/camera.png" /></div>
        <div class="cameraTag" id="cameraClickableTag"></div>
    </div>
    <div id="notSupported" class="hidden">Sorry but your browser does not support WebGL...</div>
    <script src='loadercustoms.js'></script>
</body>
</html>
  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)
        }
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值