0


分享babylon.js实现Web三维场景

前言

Web页面中创建一个虚拟的三维世界,需要一个场景Scene,并在场景中添加模型Model,模型有可能是一个简单的立方体,也可能是一个复杂的角色,无论是简单的模型,还是复杂的模型,大都是由网格Mesh组成。除此之外,还需要一个相机Camera去观察虚拟世界,一个灯光Light去照亮场景等等,拥有了上述内容后,才能够在Web页面观察到一个虚拟的三维世界。

Web应用相较于基于C/S架构的独立的应用,有其不可替代的优势:用户无须额外下载独立应用,使用浏览器即可完成体验,也存在目前难以消除的短板:大量的3D资源下载需要用户等待。

JavaScript游戏引擎或3D图形框架Babylon.js实现Web端展示3D模型。

场景Scene表示一个虚拟的场地,一般由环境、房间、道具、角色等共同组成一个虚拟的场景,在有些游戏引擎或3D框架中也叫着舞台Stage。从3D的角度来说,场景是将一些网格Mesh放在一起供用户观看,并且会在其中加入相机Camera和灯光Light让用户能够看见,就像摄影棚那样的场景一样。

场景可能还会包含一些别的元素,例如GUI用户界面,让用户能够与场景产生交互。

效果截图

使用三维软件Blender构建海鱼的三维模型,并增加材质贴图和模型动画。

信息监视Inspector面板可以看到场景的节点Nodes信息、材质Materials信息、纹理Textures、动画Animation信息等,可以在鼠标、键盘、手柄、触摸屏、眼球跟踪、收拾识别、动作识别中关联三维场景的交互。

实现代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/babylon.max.js"></script>
    <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://cdn.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://cdn.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.js"></script>
    <script src="https://cdn.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://cdn.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.js"></script>
    <script src="https://cdn.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://cdn.babylonjs.com/serializers/babylonjs.serializers.js"></script>
    <script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://cdn.babylonjs.com/gui/babylon.gui.js"></script>
    <script src="https://cdn.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <script src="https://cdn.babylonjs.com/inspector/babylon.inspector.bundle.max.js"></script>
    <script src="https://cdn.babylonjs.com/viewer/babylon.viewer.js"></script>
    <script src="https://cdn.babylonjs.com/viewer/babylon.viewer.max.js"></script>

    <style>
        body {
          padding: 0;
          margin: 0;
          font: normal 14px/1.42857 Tahoma;
        }
        #renderCanvas { width: 100vw; height: 100vh;}
    </style>
    <title>Babylon.js viewer (v7.3.1) - WebGL2 - Parallel shader compilation</title>
</head>
<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        const canvas = document.getElementById("renderCanvas");
        const engine = new BABYLON.Engine(canvas, true);

        const createBoxScene = function() {
            const scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.Black;

            const alpha =  Math.PI/4;
            const beta = Math.PI/3;
            const radius = 8;
            const target = new BABYLON.Vector3(0, 0, 0);

            const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
            camera.attachControl(canvas, true);

            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));

            const box = BABYLON.MeshBuilder.CreateBox("box", {});
            box.position.x = 0.5;
            box.position.y = 1;

            const boxMaterial = new BABYLON.StandardMaterial("material", scene);
            boxMaterial.diffuseColor = BABYLON.Color3.Random();
            box.material = boxMaterial;

            box.actionManager = new BABYLON.ActionManager(scene);
            box.actionManager.registerAction(
                new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, 
                function (evt) {
                    const sourceBox = evt.meshUnderPointer;
                    sourceBox.position.x += 0.1;
                    sourceBox.position.y += 0.1;

                    boxMaterial.diffuseColor = BABYLON.Color3.Random();
                }));

            return scene;
        };
        
        const createWaterScene = function() {
            let scene = new BABYLON.Scene(engine);
            // Camera
            let camera = new BABYLON.ArcRotateCamera("Camera", 3 * Math.PI / 2, Math.PI / 2.5, 50, BABYLON.Vector3.Zero(), scene);
            camera.attachControl(canvas, true);
            // Light
            var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
            // Skybox
            var skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, scene);
            var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
            skyboxMaterial.backFaceCulling = false;
            skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://minio.cnbabylon.com/public/Assets/TropicalSunnyDay", scene);
            skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
            skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
            skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
            skyboxMaterial.disableLighting = true;
            skybox.material = skyboxMaterial;

            //红色光
            const light1 = new BABYLON.SpotLight("spotLight1", new BABYLON.Vector3(-Math.cos(Math.PI),1,-Math.sin(Math.PI/6)),new BABYLON.Vector3(0,-1,0),Math.PI/2,1.5,scene)
            light1.diffuse = new BABYLON.Color3(1,0,0)

            //绿色光
            const light2 = new BABYLON.SpotLight("spotLight2", new BABYLON.Vector3(0,1,1 - Math.sin(Math.PI/6)),new BABYLON.Vector3(0,-1,0),Math.PI/2,1.5,scene)
            light2.diffuse = new BABYLON.Color3(0,1,0)

            //蓝色光
            const light3 = new BABYLON.SpotLight("spotLight3", new BABYLON.Vector3(Math.cos(Math.PI/6), 1,-Math.sin(Math.PI/6)),new BABYLON.Vector3(0,-1,0),Math.PI/2,1.5,scene)
            light3.diffuse = new BABYLON.Color3(0,0,1)

            const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4, height: 4}, scene);
            ground.position.y = 12;
            
            // Water material
            var waterMaterial = new BABYLON.WaterMaterial("waterMaterial", scene, new BABYLON.Vector2(512, 512));
            //设置水面的纹理贴图            
            waterMaterial.bumpTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/waterbump.png", scene);
            waterMaterial.diffuseTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/waterbump.png",scene);      
            //设置风力,决定了水波运动速度
            waterMaterial.windForce = -10;
            //设置风向 x y
            waterMaterial.windDirection = new BABYLON.Vector2(10, 10);
            //设置水的波浪高度
            waterMaterial.waveHeight = 0.5;
            //设置水纹理高度 凸起高度
            waterMaterial.bumpHeight = 0.1;
            //设置波浪的长度
            waterMaterial.waveLength = 0.1;
            //设置波浪运行速度
            waterMaterial.waveSpeed = 50.0;
            //设置水的颜色
            waterMaterial.waterColor = new BABYLON.Color3(0.9,0.1,0.2)
            //自发光
            waterMaterial.emissiveColor = new BABYLON.Color3(0.8,0.13,0.45)
            //环境光
            waterMaterial.ambientColor = new BABYLON.Color3(1, 1, 1);
            //散射光
            waterMaterial.diffuseColor = new BABYLON.Color3(1, 1, 1);
            //镜面光
            waterMaterial.specularColor = new BABYLON.Color3(0, 0, 0);

            waterMaterial.windDirection = new BABYLON.Vector2(1, 1);
            waterMaterial.colorBlendFactor = 0;
            waterMaterial.freeze(); // 冻结材质,优化渲染速度

            // Ground
            var groundTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/sand.jpg", scene);
            groundTexture.vScale = 4.0;
            groundTexture.uScale = 4.0;
            var groundMaterial = new BABYLON.StandardMaterial("groundMaterial", scene);
            groundMaterial.diffuseTexture = groundTexture;

            var ground1 = BABYLON.Mesh.CreateGround("ground1", 512, 512, 1, scene, false);
            ground1.position.y = -2;
            ground1.material = groundMaterial;

            // Water mesh
            var waterMesh = BABYLON.Mesh.CreateGround("waterMesh", 512, 512, 1, scene, false);
            waterMesh.material = waterMaterial;

            // Sphere
            var sphereMaterial = new BABYLON.StandardMaterial("sphereMaterial", scene);
            sphereMaterial.diffuseTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/wood.jpg", scene);

            var sphere = BABYLON.Mesh.CreateSphere("sphere", 16, 10, scene);
            sphere.position.y = 10;
            sphere.material = sphereMaterial;

            var sphere2 = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);  
            sphere2.position = new BABYLON.Vector3(0, 12, 0);
            var material2 = new BABYLON.StandardMaterial("bab5", scene);  
            material2.emissiveColor = new BABYLON.Color3(0, 0.4, 0); 
            sphere2.material = material2; 

            // 网格地板初始化 添加地面
            var plane = BABYLON.MeshBuilder.CreateDisc("ground", {radius: 60}, scene);
            plane.rotation.x = Math.PI / 2;
            plane.position = new BABYLON.Vector3(0, 30, 0);
            plane.freezeWorldMatrix()
            
            var materialPlane = new BABYLON.StandardMaterial("texturePlane", this.scene);
            materialPlane.diffuseColor = new BABYLON.Color3(0.0, 0.7, 0.95); //漫射色
            materialPlane.bumpTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/waterbump.png", this.scene);  //凹凸贴图
            materialPlane.emissiveColor = new BABYLON.Color3(.2, .8, .3); //发光色
            materialPlane.specularColor = new BABYLON.Color3(0.8, 0.2, 0.7);  //反射色
            materialPlane.diffuseTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/waterbump.png", this.scene)
            materialPlane.diffuseTexture.hasAlpha = true; //显示为透明
            //materialPlane.wireframe = true  // 显示网格
            materialPlane.diffuseTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/waterbump.png", this.scene); // 使用贴图
            materialPlane.diffuseTexture.uScale = 5.0;//垂直方向重复5次
            materialPlane.diffuseTexture.vScale = 5.0;//水平方向重复5次
            materialPlane.reflectionTexture = new BABYLON.Texture("https://minio.cnbabylon.com/public/Assets/waterbump.png", this.scene); // 反射贴图
            materialPlane.reflectionTexture.coordinatesMode = BABYLON.Texture.SPHERICAL_MODE; // 反射
            materialPlane.alpha = 0.6;  //调整透明度
            materialPlane.backFaceCulling = false; //使透明在背面也显示贴图
            materialPlane.backFaceCulling = false;//Allways show the front and the back of an element
            //materialPlane.freeze(); // 冻结材质,优化渲染速度

            plane.material = materialPlane;

            // Configure water material 增加到水材质中,才能正常显示水 把需要透射在水面的东西添加到render层里
            waterMaterial.addToRenderList(ground);
            waterMaterial.addToRenderList(ground1);
            waterMaterial.addToRenderList(skybox);
            waterMaterial.addToRenderList(sphere);
            waterMaterial.addToRenderList(sphere2);
            waterMaterial.addToRenderList(plane);
            
            const shadowGenerator = new BABYLON.ShadowGenerator(1024,light1)
            shadowGenerator.getShadowMap().renderList.push(sphere)
            shadowGenerator.getShadowMap().renderList.push(sphere2)
            // 地板接收阴影投射
            ground.receiveShadows = true 
            // 泊松采样过滤
            shadowGenerator.usePoissonSampling= true 
            // 指数阴影纹理
            shadowGenerator.useExponentialShadowMap= true 
            // 模糊指数阴影纹理
            shadowGenerator.useBlurExponentialShadowMap= true 
            
            let samplesNum = 100
            
            var vls = new BABYLON.VolumetricLightScatteringPostProcess('vls',1.0,camera,sphere2,samplesNum,BABYLON.Texture.BILINEAR_SAMPLINGMODE,engine,false)

            BABYLON.SceneLoader.LoadAssetContainer("https://minio.cnbabylon.com/public/Assets/", "fish.glb", this.scene, function (container) {
                // Scale and position the loaded model (First mesh loaded from gltf is the root node)
                container.meshes[0].scaling.scaleInPlace(1)
                container.meshes[0].position.z = 2
                container.meshes[0].position.y = -8
                    for (var index = 0; index <  container.meshes.length; index++) {
                        waterMaterial.addToRenderList(container.meshes[index]);
                    }
                // Add loaded file to the scene
                container.addAllToScene();
            });
            // RAY CAST TO FIND WATER HEIGHT
            //var angle = 0;
            let i = 0;
            scene.registerBeforeRender(function() {
                let time = waterMaterial._lastTime / 100000;
                let x = sphere.position.x;
                let z = sphere.position.z;
                sphere.position.y = Math.abs((Math.sin(((x / 0.05) + time * waterMaterial.waveSpeed)) * waterMaterial.waveHeight * waterMaterial.windDirection.x * 5.0) + (Math.cos(((z / 0.05) +  time * waterMaterial.waveSpeed)) * waterMaterial.waveHeight * waterMaterial.windDirection.y * 5.0));
            });
           return scene
        }

        //const sceneToRender = createBoxScene();
        const sceneToRender = createWaterScene();
        
        engine.runRenderLoop(function(){
            sceneToRender.render();
        });
        
    </script>
</body>
</html>

参见:

Babylon.js 简介和 WebXR 教程 - Mixed Reality | Microsoft Learn

与 3D 对象交互的 Babylon.js 教程 - Mixed Reality | Microsoft Learn

Babylon.js工具链

标签: javascript

本文转载自: https://blog.csdn.net/david_232656/article/details/138082295
版权归原作者 :MNongSciFans 所有, 如有侵权,请联系我们删除。

“分享babylon.js实现Web三维场景”的评论:

还没有评论