使用ThreeJs从零开始构建3D智能仓库——第一章(一切的基础)

写在前面

最近因为项目需求,需要构建一个3D仓库,能够显示整个仓库的布局以及库内货物的情况。于是刚入职两年的小白我毅然决然接下了这个沉重的任务(手动狗头),但是我并没有任何关于3D开发的经验,于是乎就各种翻书查百度,经过几个月孤独的开发与实践,已经能够初步的完成预期的设想了。
先来给大家看一下仓库的整体面貌
2019.11.26 更新:我最近建立了个人网站,大家可以访问下面的链接查看演示
3D仓库演示
2019.11.28 更新:代码和图片资源等已上传至GitHub
https://github.com/xiao149/ThreeJsDemo
在这里插入图片描述
再来一些细节的,鼠标单击一个物体,可以在物体周围高亮一圈白光突出显示,可以显示一个标签做描述
在这里插入图片描述
每一个库位都是独立可选中的
在这里插入图片描述
更有趣的是双击画面上的箱子(也就是货物啦),可以弹出详细的货物信息,比如下图托盘上就叠了好几层箱子,选中某一个箱子还可以显示货物的详细详细,比如数量批号之类的
在这里插入图片描述
当然我还很闲地给门做了个动画,可以实现开门和关门的动作,kono下图哒(虽然只是张静态的)
在这里插入图片描述

如何实现

终于到了最关键的时候呢,既然展示完了,就当然地要给大家如何完成这一切的步骤啦。这是从零开始构建3D仓库的第一篇,所以我想从一个从未接触过ThreeJs的小白的角度来开始介绍。

ThreeJs是一个面向网页端的3D设计框架,借用这个Js你可以将精致的3D建模、特效、交互、物理系统统统搬到网页上,理论上,你可以在浏览器上开发一整个3D游戏,当然我只是个初学者,向来也只是自学的,能够教给大家的很有限,这是ThreeJs官网的地址,点此进入,上面有很多有用的示例和开发文档,不过英语水平可能得好点,国内也有中文的网站不过更新的比较慢我这就不给了,大家可以自行百度。

从最简单的开始,构建地面

无论怎样的高楼大厦都是从一砖一瓦一步步搭建的,我们的3D仓库也一样,虽然这是最简单的功能(如下图),但却包含了最基本的要素:场景,相机,光照,物体,渲染器,控制器。接下来我将一一为大家介绍。

准备——浏览器跨域设置

我们在本地开发的时候经常遇到跨域的问题,即在html中无法读取到本地存放的图片等等,下图展示了跨域问题出现的时候
在这里插入图片描述
可见门,窗户,地板等有贴图的地方全部变成了空白,F12调试模式下可以看到跨域错误的具体报错信息,解决方法很简单,就是在谷歌浏览器快捷方式下右键属性,在快捷方式的目标下,加入 --allow-file-access-from-files,注意最前面有一个空格,如下图所示
在这里插入图片描述
完成后重新打开浏览器,就可以看到跨域问题已经解决啦,门窗地板全部显示出来啦!
在这里插入图片描述

初始化场景、相机、灯光、渲染器

这部分代码比较简单,而且不同的项目基本都类似,所以我就直接放出代码了,不再做过多介绍。
完成之后能够显示背景为蓝色的一个场景,当然里面什么都没有,因为我们还没有添加物体。

var stats = initStats();
var scene, camera, renderer, light;

// 初始化场景
function initScene() {
   scene = new THREE.Scene();
}

// 初始化相机
function initCamera() {
   camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
   camera.position.set(0, 800, 1500);
   camera.lookAt(new THREE.Vector3(0, 0, 0));
}

// 初始化灯光
function initLight() {
   var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.3 );//模拟远处类似太阳的光源
   directionalLight.color.setHSL( 0.1, 1, 0.95 );
   directionalLight.position.set( 0, 200, 0).normalize();
   scene.add( directionalLight );

   var ambient = new THREE.AmbientLight( 0xffffff, 1 ); //AmbientLight,影响整个场景的光源
   ambient.position.set(0,0,0);
   scene.add( ambient );
}

// 初始化性能插件
function initStats() {
   var stats = new Stats();

   stats.domElement.style.position = 'absolute';
   stats.domElement.style.left = '0px';
   stats.domElement.style.top = '0px';

   document.body.appendChild(stats.domElement);
   return stats;
}

// 初始化渲染器
function initRenderer() {
   renderer = new THREE.WebGLRenderer({antialias: true});
   renderer.setSize(window.innerWidth, window.innerHeight);
   renderer.setClearColor(0x4682B4,1.0);
   document.body.appendChild(renderer.domElement);
}

创建地板

同样废话不多说,直接上代码!

//创建地板
function createFloor(){
   var loader = new THREE.TextureLoader();
   loader.load("./ThreeJs/images/floor.jpg",function(texture){
       texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
       texture.repeat.set( 10, 10 );
       var floorGeometry = new THREE.BoxGeometry(2600, 1400, 1);
       var floorMaterial = new THREE.MeshBasicMaterial( { map: texture, side: THREE.DoubleSide } );
       var floor = new THREE.Mesh(floorGeometry, floorMaterial);
       floor.position.y = -0.5;
       floor.rotation.x = Math.PI / 2;
       floor.name = "地面";
       scene.add(floor);
   });
}

这里使用了本地的图片作为地板的贴图素材,大家可以自定义自己需要的图片。完成之后大概是这个样子
在这里插入图片描述
光秃秃的背景上出现了一个光秃秃的地面。(我变秃了也变强了)

创建控制器及其他

完成上面的步骤,我们发现场景还无法旋转、平移、放大缩小等。这些就要依赖我们的控制器了,ThreeJs官网上有很多类型的控制器,我们这里选用比较常用的轨道球控制器,代码很简单。

// 初始化轨迹球控件
function initControls() {
   controls = new THREE.OrbitControls( camera, renderer.domElement );
   controls.enableDamping = true;
   controls.dampingFactor = 0.5;
   // 视角最小距离
   controls.minDistance = 100;
   // 视角最远距离
   controls.maxDistance = 5000;
   // 最大角度
   controls.maxPolarAngle = Math.PI/2.2;
}

这里设置了最大的旋转角度,防止某些绅士看到人家下面呢!

完整的代码

之前我们分模块或者功能讲解了每一部分的代码,这一节里我贴出全部的代码,是可以直接运行的哦,相关引入的JS在ThreeJS官网上都有。

<!DOCTYPE html>
<html>
<head includeDefault="true">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <title>3D库图显示</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="./ThreeJs/three.js"></script>
    <script src="./ThreeJs/stats.min.js"></script>
    <script src="./ThreeJs/OBJLoader.js"></script>
    <script src="./ThreeJs/OrbitControls.js"></script>
</head>
<body>
    <div id="container"></div>

    <script>

        var stats = initStats();
        var scene, camera, renderer, controls, light;
        
        // 初始化场景
        function initScene() {
            scene = new THREE.Scene();
            scene.fog = new THREE.Fog( scene.background, 3000, 5000 );
        }

        // 初始化相机
        function initCamera() {
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
            camera.position.set(0, 800, 1500);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
        }

        // 初始化灯光
        function initLight() {
            var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.3 );//模拟远处类似太阳的光源
            directionalLight.color.setHSL( 0.1, 1, 0.95 );
            directionalLight.position.set( 0, 200, 0).normalize();
            scene.add( directionalLight );

            var ambient = new THREE.AmbientLight( 0xffffff, 1 ); //AmbientLight,影响整个场景的光源
            ambient.position.set(0,0,0);
            scene.add( ambient );
        }

        // 初始化性能插件
        function initStats() {
            var stats = new Stats();

            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.body.appendChild(stats.domElement);
            return stats;
        }

        // 初始化渲染器
        function initRenderer() {
            renderer = new THREE.WebGLRenderer({antialias: true});
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setClearColor(0x4682B4,1.0);
            document.body.appendChild(renderer.domElement);
        }

        //创建地板
        function createFloor(){
            var loader = new THREE.TextureLoader();
            loader.load("./ThreeJs/images/floor.jpg",function(texture){
                texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
                texture.repeat.set( 10, 10 );
                var floorGeometry = new THREE.BoxGeometry(2600, 1400, 1);
                var floorMaterial = new THREE.MeshBasicMaterial( { map: texture, side: THREE.DoubleSide } );
                var floor = new THREE.Mesh(floorGeometry, floorMaterial);
                floor.position.y = -0.5;
                floor.rotation.x = Math.PI / 2;
                floor.name = "地面";
                scene.add(floor);
            });
        }

        // 初始化模型
        function initContent() {
            createFloor();
        }

        // 初始化轨迹球控件
        function initControls() {
            controls = new THREE.OrbitControls( camera, renderer.domElement );
            controls.enableDamping = true;
            controls.dampingFactor = 0.5;
            // 视角最小距离
            controls.minDistance = 100;
            // 视角最远距离
            controls.maxDistance = 5000;
            // 最大角度
            controls.maxPolarAngle = Math.PI/2.2;
        }

        // 更新控件
        function update() {
            stats.update();
            controls.update();
        }

        // 初始化
        function init() {
            initScene();
            initCamera();
            initRenderer();
            initContent();
            initLight();
            initControls();
            document.addEventListener('resize', onWindowResize, false);
        }

        // 窗口变动触发的方法
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function animate() {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
            update();
        }

        init();
        animate();
    </script>
</body>
</html>

完成之后就可以看到一个可以随意转动移动缩放的地面啦!

结束语

第一章我们涉及的内容都比较简单,但也是最为基础关键的,致此我们创建好了一个可以控制的地面,下一章我们将创建墙面,门,窗户,以及选中物体的交互事件。
我跟广大学习ThreeJs的初学者一样,仍带着懵懂的心去探索这片新大陆,CSDN上的许多前辈都给了我很多关键的灵感和技术方法,如果大家有兴趣,也可以互相交流成长,欢迎大家指导咨询。PS:大家有兴趣可以点进去我的头像,陆陆续续也写了十来篇了。
链接:使用ThreeJs从零开始构建3D智能仓库——第一章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第二章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第三章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第四章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第五章: 点我跳转.

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页