Three.js基础+案例(二)

3,587 阅读15分钟

src=http___ddrvcn.oss-cn-hangzhou.aliyuncs.com_2019_8_QZBjm2.png&refer=http___ddrvcn.oss-cn-hangzhou.aliyuncs.jpeg

一、学习收获

1、Three.js 相机插件的使用

2、Three.js 基本元素 :精灵(Sprite)

3、Three.js 导出和导入JSON格式的3D模型

4、Three.js 导出和导入glTF格式的3D模型

5、Three.js 代码封装

二、主要内容:

1、Three.js中的相机插件

src=http___pic2.zhimg.com_v2-c5cb61c51efa03c52ab5e1e8129454ad_r.jpg&refer=http___pic2.zhimg.jpeg

通过Three.js的相机控件OrbitControls.js可以对Threejs的三维场景进行缩放、平移、旋转操作,本质上改变的并不是场景,而是相机的参数,相机的位置角度不同,同一个场景的渲染效果是不一样,比如一个相机绕着一个场景旋转,就像场景旋转一样。OrbitControls.js是对Three.js中正交投影相机和透视投影相机对象进行了封装。

简言之:在Three.js中能够使用OrbitControls.js进行自由视角观察。

OrbitControls.js链接: github.com/mrdoob/thre…

使用OrbitControls.js

  1. 引入OrbitControls.js插件
<script src="./OrbitControls.js"></script>
  1. 实例化相机插件对象
var control = new THREE.OrbitControls(camera, renderer.domElement);
  1. 在每一帧执行update(可以实时更新一下网格的位置,这样效果更好)
function animate() {
    //  循环调用函数
    requestAnimationFrame(animate);
    // 每一次animate函数调用,都让网格比上一次 X 轴、Y 轴各旋转增加 0.01 弧度
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    //  更新性能插件
    stats.update();
  	// cube.position.x = 3; //将模型的位置调整到x正轴距离原点为3的位置。
  	// cube.position.y += 5; //将模型的y轴位置以当前的位置向上移动5个单位。
    // cube.position.z -= 6; //将模型的Z轴位置以当前的位置向后移动5个单位。
  	// 更新网格的位置
   	cube.position.set(2, 5, -6);
  	// 更新相机插件
   	control.update();
    // 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
    renderer.render(scene, camera);
};
// 调用动画函数
animate();
  1. 案例截图:

image-20191226203029273

  1. 完整代码如下:
<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 OrbitControls.js-->
    <script src="./OrbitControls.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );
        // 2.1 设置相机位置
        // camera.position.x = 5;
        // camera.position.y = 10;
        // camera.position.z = 10;
        // 2.1 设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);

        // 实例化相机插件对象
        var control = new THREE.OrbitControls(camera, renderer.domElement);
        // 4、创建几何体模型(立方几何体)
        var geometry = new THREE.BoxGeometry(4, 4, 4);

        // 5、创建材质(基础网格材质和法线网格材质)
        // 5.1 创建基础网格材质
        var materialBasic = new THREE.MeshBasicMaterial({
            color: 0xffffff, // 白色
            wireframe: true //是否将几何体渲染为线框,默认值为false(即渲染为平面多边形)
        });
        // 5.2 创建法线网格材质
        var materialNormal = new THREE.MeshNormalMaterial();

        // 6、创建多种网格(因为有多个材质)
        // 第一个参数是几何模型,第二参数是材质
        var cube = THREE.SceneUtils.createMultiMaterialObject(geometry, [
            materialBasic,
            materialNormal
        ]);
        // 6.1 让相机 看向(对着)物体(拍摄对象)的位置(默认状态下,相机将指向三维坐标系的原点。)
        camera.lookAt(cube.position);
        // 6.2、将网格添加到场景中
        scene.add(cube);
        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);
        // 8、为了方便观察3D图像,添加三维坐标系对象
        var axes = new THREE.AxisHelper(6);
        scene.add(axes);
        // 10、实例化性能监视插件
        var stats = new Stats();
        // 10.1 把stats对象生成的dom,添加到页面中(这样就能在页面中看到性能监视器了)
        document.body.appendChild(stats.dom);
        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);
            // 每一次animate函数调用,都让网格比上一次 X 轴、Y 轴各旋转增加 0.01 弧度
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            // 10.2 更新性能插件
            stats.update();
            // cube.position.x = 3; //将模型的位置调整到x正轴距离原点为3的位置。
            // cube.position.y += 5; //将模型的y轴位置以当前的位置向上移动5个单位。
            // cube.position.z -= 6; //将模型的Z轴位置以当前的位置向后移动5个单位。
            cube.position.set(2, 5, -6);
            // 更新相机插件
            control.update();
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();
    </script>
</body>

</html>

2、THREE.Object3D

为了方便操作,Three.js将每个能够直接添加到场景内的对象都继承至一个基类-THREE.Object3D,以后我们将继承至这个基类的对象称为3d对象。Object3D是ThreeJS中大部分物体的基类,它包含了物体的位移,旋转,缩放,以及各个物体父子关系的js实现。

判断一个对象是否是继承至THREE.Object3D,我们可以这么判断:

obj instanceof THREE.Object3D
//如果一个对象继承至THREE.Object3D,则返回 true 否则返回false

image-20191224151339286

添加一个3d对象:

将一个网格添加到场景中进行使用

scene.add(cube);; //将网格添加到场景

删除一个3d对象

如果一个模型不再被使用到,需要彻底删除掉,我们可以使用remove方法进行删除:

scene.remove(mesh); //将一个模型从场景中删除

3、Three.js内容补充

3.1、修改位置的3种方式

  1. 单独设置
camera.position.x = 5;
camera.position.y = 10;
camera.position.z = 10;
  1. 使用set方法,一次性设置所有
camera.position.set(5, 10, 10);
  1. 通过三维向量一次性设置

    Three.js的模型的位置属性是一个THREE.Vector3(三维向量)的对象,

    一个三维向量表示的是一个有顺序的、三个为一组的数字组合(标记为x、y和z), 可被用来表示很多事物,例如:

    • 一个位于三维空间中的点。
    • 一个在三维空间中的方向与长度的定义。在three.js中,长度总是从(0, 0, 0)到(x, y, z)的直线距离,方向也是从(0, 0, 0)到(x, y, z)的方向。
camera.position = new THREE.Vector3(5, 10, 10); //上面的设置位置也可以通过这样设置。

3.2、修改大小的3种方式

  1. 单独设置
cube.scale.x = 2; //模型沿x轴放大一倍
cube.scale.y = 0.5; //模型沿y轴缩小一倍
cube.scale.z = 1; //模型沿z轴保持不变
  1. 使用set方法,一次性设置所有
cube.scale.set(2, 2, 2); //每个方向等比放大一倍
  1. 由于scale属性也是一个三维向量,通过三维向量一次性设置
cube.scale = new THREE.Vector3(2, 2, 2); //每个方向都放大一倍

3.3 修改模型转向(旋转)的3种方式

  1. 单独设置每个轴的旋转:
cube.rotation.x = Math.PI; //模型沿x旋转180度
cube.rotation.y = Math.PI * 2; //模型沿y轴旋转360度,跟没旋转一样的效果。。。
cube.rotation.z = - Math.PI / 2; //模型沿z轴逆时针旋转90度
  1. 使用set方法,一次性设置所有
cube.rotation.set(Math.PI, 0, - Math.PI / 2); //旋转效果和第一种显示的效果相同
  1. 通过重新赋值一个欧拉角对象来实现旋转调整【了解即可】

    模型的rotation属性其实是一个欧拉角对象(THREE.Euler),欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体。

cube.rotation = new THREE.Euler(Math.PI, 0, - Math.PI / 2, 'XYZ'); 

3、Three.js 基本元素 :精灵(Sprite)和 粒子(Points)

1、精灵(Sprite)

精灵(Sprite )属于Three.js中的物体,但是我们通过相机查看它时,始终看到的是它的正面,它总是朝向相机。通过它的这种特性,我们可以实现广告牌的效果,或实现更多的比如雨雪、烟雾等更加绚丽的特效。

注意:人有正面、背面、侧面的,但是雨、雪、烟雾等物体是不分正面、背面、侧面的,它们只有一个面。

**精灵(Sprite)**由于一直正对着相机的特性,一般使用在模型的提示信息当中。通过THREE.Sprite创建生成,由于THREE.Sprite和THREE.Mesh都属于THREE.Object3D的子类,所以,我们操作模型网格的相关属性和方法大部分对于精灵都适用。和精灵一起使用的还有一个THREE.SpriteMaterial对象,它是专门配合精灵的材质。注意:精灵没有阴影效果。

  1. 普通方式的精灵(Sprite)
// 创建精灵材质
var spriteMaterialNormal = new THREE.SpriteMaterial({color: 0x00ffff});
// 实例化精灵对象
var spriteNormal = new THREE.Sprite(spriteMaterialNormal);
//设置精灵对象的位置
spriteNormal.position.set(-30, 10, 0); 
//设置精灵对象的缩放大小
spriteNormal.scale.set(5, 5, 1);  
// 在场景中添加精灵
scene.add(spriteNormal);

1.0 案例截图:

image-20191226202415603

1.1 完整代码如下:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 OrbitControls.js-->
    <script src="./OrbitControls.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );
        // 2.1 设置相机位置
        // camera.position.x = 5;
        // camera.position.y = 10;
        // camera.position.z = 10;
        // 2.1 设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);

        // 实例化相机插件对象
        var control = new THREE.OrbitControls(camera, renderer.domElement);
        // 4、创建几何体模型(立方几何体)
        var geometry = new THREE.BoxGeometry(4, 4, 4);

        // 5、创建材质(基础网格材质和法线网格材质)
        // 5.1 创建基础网格材质
        var materialBasic = new THREE.MeshBasicMaterial({
            color: 0xffffff, // 白色
            wireframe: true //是否将几何体渲染为线框,默认值为false(即渲染为平面多边形)
        });
        // 5.2 创建法线网格材质
        var materialNormal = new THREE.MeshNormalMaterial();
        // 6、创建多种网格(因为有多个材质)
        // 第一个参数是几何模型,第二参数是材质
        var cube = THREE.SceneUtils.createMultiMaterialObject(geometry, [
            materialBasic,
            materialNormal
        ]);
        // 6.1 让相机 看向(对着)物体(拍摄对象)的位置(默认状态下,相机将指向三维坐标系的原点。)
        camera.lookAt(cube.position);
        // 6.2、将网格添加到场景中
        scene.add(cube);
        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);
        // 8、为了方便观察3D图像,添加三维坐标系对象
        var axes = new THREE.AxisHelper(6);
        scene.add(axes);
        // 10、实例化性能监视插件
        var stats = new Stats();
        // 10.1 把stats对象生成的dom,添加到页面中(这样就能在页面中看到性能监视器了)
        document.body.appendChild(stats.dom);
        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);
            // 每一次animate函数调用,都让网格比上一次 X 轴、Y 轴各旋转增加 0.01 弧度
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            // 10.2 更新性能插件
            stats.update();
            // cube.position.x = 3; //将模型的位置调整到x正轴距离原点为3的位置。
            // cube.position.y += 5; //将模型的y轴位置以当前的位置向上移动5个单位。
            // cube.position.z -= 6; //将模型的Z轴位置以当前的位置向后移动5个单位。
            cube.position.set(2, 5, -6);
            // 更新相机插件
            control.update();
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();
        // 11 创建精灵材质
        var spriteMaterialNormal = new THREE.SpriteMaterial({
            color: 0x00ffff
        });
        // 11.1 实例化精灵对象
        var spriteNormal = new THREE.Sprite(spriteMaterialNormal);
        //11.2 设置精灵对象的位置
        spriteNormal.position.set(-30, 0, 0);
        //11.3 设置精灵对象的缩放大小
        spriteNormal.scale.set(5, 5, 1);
        // 11.4 在场景中添加精灵
        scene.add(spriteNormal);
    </script>
</body>

</html>
  1. 图片(TextureLoader)导入的方式

    (TextureLoader内部使用ImageLoader来加载文件)。

    此导入方式需要借助Canvas来实现。

    				//创建canvas对象(封装Canvas画布)
            function drawCanvas(options) {
                // 创建Canvas画布
                var canvas = document.createElement("canvas");
                // 设置Canvas画布的宽度和高度
                canvas.width = options.width;
                canvas.height = options.height;
                // 设置 在Canvas画布上的绘图环境为2D绘图
                var ctx = canvas.getContext("2d");
                // 设置 绘图环境的背景颜色填充颜色为黑色
                ctx.fillStyle = "rgba(0, 0, 0, 0)";
                // 绘制“被填充”的矩形(绘制矩形,该矩形的填充颜色为黑色)
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                // ctx.fillRect(x,y,width,height);
                // 参数    描述
                // x      矩形左上角的 x 坐标
                // y      矩形左上角的 y 坐标
                // width  矩形的宽度, 以像素计
                // height 矩形的高度, 以像素计
                // 设置绘图环境的字体大小和字体(Verdana 为无衬线字体)
                ctx.font = "60px Verdana";
                // 设置绘图中物体的颜色为白色(在此处设置的是文本的颜色)
                ctx.fillStyle = "#fff";
                // 在画布上绘制填色的文本
                ctx.fillText(options.text, 0, 56, options.width);
                // ctx.fillText(text,x,y,maxWidth);
                // 参数	      描述
                // text      规定在画布上输出的文本。
                // x         开始绘制文本的 x 坐标位置( 相对于画布)。
                // y         开始绘制文本的 y 坐标位置( 相对于画布)。
                // maxWidth  可选。 允许的最大文本宽度, 以像素计。
                return canvas; // 返回画布给封装好的drawCanvas函数
            }
    
// 通过TextureLoader将图片转为base64格式(为了减小图片的体积)
var spriteMap = new THREE.TextureLoader().load(drawCanvas({text: "文本内容", width: 64, height: 64}).toDataURL()); 
// 创建精灵材质
var spriteMaterial = new THREE.SpriteMaterial({map: spriteMap, color: 0xffffff});
// 实例化精灵对象
var sprite = new THREE.Sprite(spriteMaterial);
//设置精灵对象的位置
sprite.position.set(0, 10, 0); //设置位置
//设置精灵对象的缩放大小
sprite.scale.set(5, 5, 1); //设置scale进行大小设置
// 在场景中添加精灵
scene.add(sprite);

2.0 案例截图:

image-20191226202549239

2.1完整代码:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 OrbitControls.js-->
    <script src="./OrbitControls.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );
        // 2.1 设置相机位置
        // camera.position.x = 5;
        // camera.position.y = 10;
        // camera.position.z = 10;
        // 2.1 设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);

        // 实例化相机插件对象
        var control = new THREE.OrbitControls(camera, renderer.domElement);
        // 4、创建几何体模型(立方几何体)
        var geometry = new THREE.BoxGeometry(4, 4, 4);

        // 5、创建材质(基础网格材质和法线网格材质)
        // 5.1 创建基础网格材质
        var materialBasic = new THREE.MeshBasicMaterial({
            color: 0xffffff, // 白色
            wireframe: true //是否将几何体渲染为线框,默认值为false(即渲染为平面多边形)
        });
        // 5.2 创建法线网格材质
        var materialNormal = new THREE.MeshNormalMaterial();
        // 6、创建多种网格(因为有多个材质)
        // 第一个参数是几何模型,第二参数是材质
        var cube = THREE.SceneUtils.createMultiMaterialObject(geometry, [
            materialBasic,
            materialNormal
        ]);
        // 6.1 让相机 看向(对着)物体(拍摄对象)的位置(默认状态下,相机将指向三维坐标系的原点。)
        camera.lookAt(cube.position);
        // 6.2、将网格添加到场景中
        scene.add(cube);
        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);
        // 8、为了方便观察3D图像,添加三维坐标系对象
        var axes = new THREE.AxisHelper(6);
        scene.add(axes);
        // 10、实例化性能监视插件
        var stats = new Stats();
        // 10.1 把stats对象生成的dom,添加到页面中(这样就能在页面中看到性能监视器了)
        document.body.appendChild(stats.dom);
        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);
            // 每一次animate函数调用,都让网格比上一次 X 轴、Y 轴各旋转增加 0.01 弧度
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            // 10.2 更新性能插件
            stats.update();
            // cube.position.x = 3; //将模型的位置调整到x正轴距离原点为3的位置。
            // cube.position.y += 5; //将模型的y轴位置以当前的位置向上移动5个单位。
            // cube.position.z -= 6; //将模型的Z轴位置以当前的位置向后移动5个单位。
            cube.position.set(2, 5, -6);
            // 更新相机插件
            control.update();
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();

        //创建canvas对象
        function drawCanvas(options) {
            // 创建Canvas画布
            var canvas = document.createElement("canvas");
            // 设置Canvas画布的宽度和高度
            canvas.width = options.width;
            canvas.height = options.height;
            // 设置 在Canvas画布上的绘图环境为2D绘图
            var ctx = canvas.getContext("2d");
            // 设置 绘图环境的背景颜色填充颜色为黑色
            ctx.fillStyle = "rgba(0, 0, 0, 0)";
            // 绘制“被填充”的矩形(绘制矩形,该矩形的填充颜色为黑色)
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            // ctx.fillRect(x,y,width,height);
            // 参数    描述
            // x      矩形左上角的 x 坐标
            // y      矩形左上角的 y 坐标
            // width  矩形的宽度, 以像素计
            // height 矩形的高度, 以像素计
            // 设置绘图环境的字体大小和字体(Verdana 为无衬线字体)
            ctx.font = "60px Verdana";
            // 设置绘图中物体的颜色为白色(在此处设置的是文本的颜色)
            ctx.fillStyle = "#fff";
            // 在画布上绘制填色的文本
            ctx.fillText(options.text, 0, 56, options.width);
            // ctx.fillText(text,x,y,maxWidth);
            // 参数	      描述
            // text      规定在画布上输出的文本。
            // x         开始绘制文本的 x 坐标位置( 相对于画布)。
            // y         开始绘制文本的 y 坐标位置( 相对于画布)。
            // maxWidth  可选。 允许的最大文本宽度, 以像素计。
            return canvas; // 返回画布给封装好的drawCanvas函数
        }
        // 通过TextureLoader将图片转为base64格式(为了减小图片的体积)
        var spriteMap = new THREE.TextureLoader().load(drawCanvas({
            text: "文本内容",
            width: 64,
            height: 64
        }).toDataURL());
        // 创建精灵材质
        var spriteMaterial = new THREE.SpriteMaterial({
            map: spriteMap,
            color: 0xffffff
        });
        // 实例化精灵对象
        var sprite = new THREE.Sprite(spriteMaterial);
        //设置精灵对象的位置
        sprite.position.set(0, 10, 0); //设置位置
        //设置精灵对象的缩放大小
        sprite.scale.set(5, 5, 1); //设置scale进行大小设置
        // 在场景中添加精灵
        scene.add(sprite);
    </script>
</body>

</html>
  1. canvas导入的方式

    需要结合使用纹理对象(Texture),用途是:创建一个纹理贴图,将其应用到一个表面。

	//创建canvas对象(封装Canvas画布)
     function drawCanvas(options) {
         // 创建Canvas画布
         var canvas = document.createElement("canvas");
         // 设置Canvas画布的宽度和高度
         canvas.width = options.width;
         canvas.height = options.height;
         // 设置 在Canvas画布上的绘图环境为2D绘图
         var ctx = canvas.getContext("2d");
         // 设置 绘图环境的背景颜色填充颜色为黑色
         ctx.fillStyle = "rgba(0, 0, 0, 0)";
         // 绘制“被填充”的矩形(绘制矩形,该矩形的填充颜色为黑色)
         ctx.fillRect(0, 0, canvas.width, canvas.height);
         // ctx.fillRect(x,y,width,height);
         // 参数    描述
         // x      矩形左上角的 x 坐标
         // y      矩形左上角的 y 坐标
         // width  矩形的宽度, 以像素计
         // height 矩形的高度, 以像素计
         // 设置绘图环境的字体大小和字体(Verdana 为无衬线字体)
         ctx.font = "60px Verdana";
         // 设置绘图中物体的颜色为白色(在此处设置的是文本的颜色)
         ctx.fillStyle = "#fff";
         // 在画布上绘制填色的文本
         ctx.fillText(options.text, 0, 56, options.width);
         // ctx.fillText(text,x,y,maxWidth);
         // 参数	      描述
         // text      规定在画布上输出的文本。
         // x         开始绘制文本的 x 坐标位置( 相对于画布)。
         // y         开始绘制文本的 y 坐标位置( 相对于画布)。
         // maxWidth  可选。 允许的最大文本宽度, 以像素计。
         return canvas; // 返回画布给封装好的drawCanvas函数
     }
// 调用drawCanvas函数传入一个特定的对象(对象中的属性要符合封装函数的需求)
var canvas = drawCanvas({
    text: "立方体",
    width: 256,
    height: 64
});
// 创建一个纹理贴图,将其应用到一个表面
var spriteMapCube = new THREE.Texture(canvas);
spriteMapCube.wrapS = THREE.RepeatWrapping;
spriteMapCube.wrapT = THREE.RepeatWrapping;
// wrapS定义了纹理贴图在水平方向上将如何包裹
// wrapT定义了纹理贴图在垂直方向上将如何包裹
// RepeatWrapping 一种包裹模式,纹理将简单地重复到无穷大。
// 如果编码类型在纹理已被一个材质使用之后发生了改变, 你需要来设置Material.needsUpdate为true来使得材质重新编译。
spriteMapCube.needsUpdate = true;
// 创建精灵材质
var spriteMaterial = new THREE.SpriteMaterial({
    map: spriteMapCube, // 将纹理贴在精灵的材质表面
    color: 0xffffff // 白色
});
// 实例化精灵对象
var spriteCube = new THREE.Sprite(spriteMaterial);
//设置精灵对象位置 
spriteCube.position.set(30, 10, -5);
//设置精灵对象缩放大小
spriteCube.scale.set(20, 5, 1);
// 将设置好的精灵对象,添加到场景中
scene.add(spriteCube);

3.0 案例截图:

image-20191226202700407

3.1 完整代码:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 OrbitControls.js-->
    <script src="./OrbitControls.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );
        // 2.1 设置相机位置
        // camera.position.x = 5;
        // camera.position.y = 10;
        // camera.position.z = 10;
        // 2.1 设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);

        // 实例化相机插件对象
        var control = new THREE.OrbitControls(camera, renderer.domElement);
        // 4、创建几何体模型(立方几何体)
        var geometry = new THREE.BoxGeometry(4, 4, 4);

        // 5、创建材质(基础网格材质和法线网格材质)
        // 5.1 创建基础网格材质
        var materialBasic = new THREE.MeshBasicMaterial({
            color: 0xffffff, // 白色
            wireframe: true //是否将几何体渲染为线框,默认值为false(即渲染为平面多边形)
        });
        // 5.2 创建法线网格材质
        var materialNormal = new THREE.MeshNormalMaterial();
        // 6、创建多种网格(因为有多个材质)
        // 第一个参数是几何模型,第二参数是材质
        var cube = THREE.SceneUtils.createMultiMaterialObject(geometry, [
            materialBasic,
            materialNormal
        ]);
        // 6.1 让相机 看向(对着)物体(拍摄对象)的位置(默认状态下,相机将指向三维坐标系的原点。)
        camera.lookAt(cube.position);
        // 6.2、将网格添加到场景中
        scene.add(cube);
        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);
        // 8、为了方便观察3D图像,添加三维坐标系对象
        var axes = new THREE.AxisHelper(6);
        scene.add(axes);
        // 10、实例化性能监视插件
        var stats = new Stats();
        // 10.1 把stats对象生成的dom,添加到页面中(这样就能在页面中看到性能监视器了)
        document.body.appendChild(stats.dom);
        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);
            // 每一次animate函数调用,都让网格比上一次 X 轴、Y 轴各旋转增加 0.01 弧度
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            // 10.2 更新性能插件
            stats.update();
            // cube.position.x = 3; //将模型的位置调整到x正轴距离原点为3的位置。
            // cube.position.y += 5; //将模型的y轴位置以当前的位置向上移动5个单位。
            // cube.position.z -= 6; //将模型的Z轴位置以当前的位置向后移动5个单位。
            cube.position.set(2, 5, -6);
            // 更新相机插件
            control.update();
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();

        //创建canvas对象
        function drawCanvas(options) {
            // 创建Canvas画布
            var canvas = document.createElement("canvas");
            // 设置Canvas画布的宽度和高度
            canvas.width = options.width;
            canvas.height = options.height;
            // 设置 在Canvas画布上的绘图环境为2D绘图
            var ctx = canvas.getContext("2d");
            // 设置 绘图环境的背景颜色填充颜色为黑色
            ctx.fillStyle = "rgba(0, 0, 0, 0)";
            // 绘制“被填充”的矩形(绘制矩形,该矩形的填充颜色为黑色)
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            // ctx.fillRect(x,y,width,height);
            // 参数    描述
            // x      矩形左上角的 x 坐标
            // y      矩形左上角的 y 坐标
            // width  矩形的宽度, 以像素计
            // height 矩形的高度, 以像素计
            // 设置绘图环境的字体大小和字体(Verdana 为无衬线字体)
            ctx.font = "60px Verdana";
            // 设置绘图中物体的颜色为白色(在此处设置的是文本的颜色)
            ctx.fillStyle = "#fff";
            // 在画布上绘制填色的文本
            ctx.fillText(options.text, 0, 56, options.width);
            // ctx.fillText(text,x,y,maxWidth);
            // 参数	      描述
            // text      规定在画布上输出的文本。
            // x         开始绘制文本的 x 坐标位置( 相对于画布)。
            // y         开始绘制文本的 y 坐标位置( 相对于画布)。
            // maxWidth  可选。 允许的最大文本宽度, 以像素计。
            return canvas; // 返回画布给封装好的drawCanvas函数
        }
      
        // 调用drawCanvas函数传入一个特定的对象(对象中的属性要符合封装函数的需求)
        var canvas = drawCanvas({
            text: "立方体",
            width: 256,
            height: 64
        });
        // 创建一个纹理贴图,将其应用到一个表面
        var spriteMapCube = new THREE.Texture(canvas);
        spriteMapCube.wrapS = THREE.RepeatWrapping;
        spriteMapCube.wrapT = THREE.RepeatWrapping;
        // wrapS定义了纹理贴图在水平方向上将如何包裹
        // wrapT定义了纹理贴图在垂直方向上将如何包裹
        // RepeatWrapping 一种包裹模式,纹理将简单地重复到无穷大。
        // 如果编码类型在纹理已被一个材质使用之后发生了改变, 你需要来设置Material.needsUpdate为true来使得材质重新编译。
        spriteMapCube.needsUpdate = true;
        // 创建精灵材质
        var spriteMaterial = new THREE.SpriteMaterial({
            map: spriteMapCube, // 将纹理贴在精灵的材质表面
            color: 0xffffff // 白色
        });
        // 实例化精灵对象
        var spriteCube = new THREE.Sprite(spriteMaterial);
        //设置精灵对象位置 
        spriteCube.position.set(30, 10, -5);
        //设置精灵对象缩放大小
        spriteCube.scale.set(20, 5, 1);
        // 将设置好的精灵对象,添加到场景中
        scene.add(spriteCube);
    </script>
</body>

</html>

2、粒子(Points)

**粒子(Points)和精灵(Sprite )**的效果是一样的,这两种对象共同点就是我们通过相机查看它们时,始终看到的是它们的正面,它们总朝向相机。它们之间的区别就是如果当前场景内的精灵过多的话,就会出现性能问题

**粒子(Points)**的作用就是为解决很多精灵而出现的,我们可以使用粒子去模拟数量很多的效果,比如下雨,下雪等,数量很多的时候就适合使用粒子来创建,相应的,提高性能的损失就是失去了对单个精灵的操作,所有的粒子的效果都是一样。总的来说,粒子就是提高性能减少的一些自由度,而精灵就是为了自由度而损失了一些性能。

但是粒子有一些特殊的情况就是THREE.Points是它们粒子个体的父元素,它的位置设置也是基于THREE.Points位置而定位,而修改THREE.Points的scale属性只会修改掉粒子个体的位置。

创建粒子

//创建球形几何体
var sphereGeometry = new THREE.SphereGeometry(5, 32, 32);
// 创建粒子材质
var sphereMaterial = new THREE.PointsMaterial({
    color: 0xff00ff,
    size: 0.1
});
// 创建粒子实例(结合几何体和材质)
// Points(几何体, 材质)
var sphere = new THREE.Points(sphereGeometry, sphereMaterial);
//  把粒子添加到场景中
scene.add(sphere);

SphereGeometry( radius ,widthSegments, heightSegments)

radius — 球体半径,默认为1。 widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为8。 heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为6。

以下是设置的SphereGeometry(3, 4, 6)模型外观

image-20191228214037777

image-20191228214218619

以上代码创建粒子效果图:

粒子会吸附在几何体的表面

image-20191228213207540

上面是一个通过球体几何体创建的一个最简单的粒子特效。

使用任何几何体都可以创建粒子的几何体,甚至自己生成的几何体都可以

比如创建星空案例:

image-20191228222459786

// 创建跟几何体的相关属性的顶点(Geometry包含顶点位置,面信息,颜色等)
var starsGeometry = new THREE.Geometry();
// 创建1万个点
for (var i = 0; i < 10000; i++) {
    //通过三维向量创建点,因为三维向量可以代表一个位于三维空间中的点。
    var star = new THREE.Vector3();
    // 设置点的随机位置
    star.x = THREE.Math.randFloatSpread(2500);
    star.y = THREE.Math.randFloatSpread(2000);
    star.z = THREE.Math.randFloatSpread(2500);
    // 把创建好的点,添加到几何体的相关属性的顶点上。
    // vertices(顶点的队列)保存了模型中每个顶点的位置信息。
    starsGeometry.vertices.push(star)
}
// 设置粒子材质
var starsMaterial = new THREE.PointsMaterial({
    color: 0x888888
})
// 创建粒子实例
var starField = new THREE.Points(starsGeometry, starsMaterial);
// 将10000个点,添加到场景中
scene.add(starField);

星空案例完整代码:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );
        // 2.1 设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);


        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);



        // 创建跟几何体的相关属性的顶点(Geometry包含顶点位置,面信息,颜色等)
        var starsGeometry = new THREE.Geometry();
        // 创建1万个点
        for (var i = 0; i < 10000; i++) {
            //通过三维向量创建点,因为三维向量可以代表一个位于三维空间中的点。
            var star = new THREE.Vector3();
            // 设置点的随机位置
            star.x = THREE.Math.randFloatSpread(2500);
            star.y = THREE.Math.randFloatSpread(2000);
            star.z = THREE.Math.randFloatSpread(2500);
            // 把创建好的点,添加到几何体的相关属性的顶点上。
            // vertices(顶点的队列)保存了模型中每个顶点的位置信息。
            starsGeometry.vertices.push(star)
        }
        // 设置粒子材质
        var starsMaterial = new THREE.PointsMaterial({
            color: 0x888888
        })
        // 创建粒子实例
        var starField = new THREE.Points(starsGeometry, starsMaterial);
        // 将1000个点,添加到场景中
        scene.add(starField);


        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();

    </script>
</body>

</html>

实现星空的转动:

var beforeDate = new Date(); //之前的时间
// console.log("beforeDate", beforeDate);

// 9、创建动画循环渲染函数
function animate() {
    var nowDate = new Date(); //现在的时间
    console.log("nowDate", nowDate);

    var t = nowDate - beforeDate; //时间差
    beforeDate = nowDate; //把现在的时间赋值给之前的时间

    //让相机转动以此来实现整个场景的旋转
    camera.rotateY(-0.0001 * t);
    camera.rotateX(0.00005 * t);
    camera.rotateZ(0.00005 * t);
    // 9.1 循环调用函数
    requestAnimationFrame(animate);
    // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
    renderer.render(scene, camera);
};
// 调用动画函数
animate();

实现星空的转动完整代码:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );
        // 2.1 设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);


        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);



        // 创建跟几何体的相关属性的顶点(Geometry包含顶点位置,面信息,颜色等)
        var starsGeometry = new THREE.Geometry();
        // 创建1万个点
        for (var i = 0; i < 10000; i++) {
            //通过三维向量创建点,因为三维向量可以代表一个位于三维空间中的点。
            var star = new THREE.Vector3();
            // 设置点的随机位置
            star.x = THREE.Math.randFloatSpread(2500);
            star.y = THREE.Math.randFloatSpread(2000);
            star.z = THREE.Math.randFloatSpread(2500);
            // 把创建好的点,添加到几何体的相关属性的顶点上。
            // vertices(顶点的队列)保存了模型中每个顶点的位置信息。
            starsGeometry.vertices.push(star)
        }
        // 设置粒子材质
        var starsMaterial = new THREE.PointsMaterial({
            color: 0x888888
        })
        // 创建粒子实例
        var starField = new THREE.Points(starsGeometry, starsMaterial);
        // 将1000个点,添加到场景中
        scene.add(starField);


var beforeDate = new Date(); //之前的时间
// console.log("beforeDate", beforeDate);

// 9、创建动画循环渲染函数
function animate() {
    var nowDate = new Date(); //现在的时间
    console.log("nowDate", nowDate);

    var t = nowDate - beforeDate; //时间差
    beforeDate = nowDate; //把现在的时间赋值给之前的时间

    //让相机转动以此来实现整个场景的旋转
    camera.rotateY(-0.0001 * t);
    camera.rotateX(0.00005 * t);
    camera.rotateZ(0.00005 * t);
    // 9.1 循环调用函数
    requestAnimationFrame(animate);
    // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
    renderer.render(scene, camera);
};
// 调用动画函数
animate();
    </script>
</body>

</html>

4、Three.js导入3D模型

复杂的3D模型(比如制作一个飞机模型)一般都是用第三方建模工具生成,然后加载到Three.js中。

4.1、常用建模制作工具

3Dmax

链接地址:www.autodesk.com.cn/products/3d…

最常见的3D建模软件,广泛应用于广告、影视、工业设计、建筑设计、三维动画、多媒体制作、游戏、辅助教学以及工程可视化等领域。

image-20191224174558250

SketchUp

链接地址:www.sketchup.com/zh-CN

SketchUp是一个极受欢迎并且易于使用的3D设计软件,官方网站将它比喻作电子设计中的“铅笔”。它的主要卖点就是使用简便,人人都可以快速上手。

image-20191224175012380

4.2、常用3D模型素材网站:

sketchupbar

链接地址:www.sketchupbar.com/default.php

image-20191224175354983

sketchfab

链接地址:sketchfab.com/

4.3、Three.js支持的3D模型格式

Three.js支持的导出格式

Three.js在线编辑器:threejs.org/editor/

Three.js支持的全部格式

github.com/mrdoob/thre…

4.4、在Three.js中导出3D模型步骤

(1)打开 Three.js在线编辑器

threejs.org/editor/

(2)点击添加按钮,选择将要添加的几何体模型

image-20191224181513486

(3)设置几何体模型的材质类型和材质颜色

image-20191224182133282

(4)设置几何体模型的属性(比如:位置、旋转、缩放)

image-20191224182430281

(5)将3D模型导出(选择导出场景,导出的是一个json格式的文件)

json格式,一般用于Three.js官方的editor导出

image-20191224182601071

4.5、在Three.js中导入3D模型步骤

(1)把下载好的json文件放入项目目录中(放入的位置随意)

(2)json文件中的JSON格式指的是Three.js可以将其转换为场景的3D对象的JSON格式模型。这种格式内部一般必有的四项为:

  • metadata 当前模型的相关信息以及生成的工具信息

  • geometries 存储当前模型所使用的几何体的数组

  • materials 存储当前模型所使用的材质的数组

  • object 当前模型的结构以及标示所应用到的材质和几何体标示

    所有的模型网格,几何体和材质都有一个固定的uuid标识符,JSON格式中都是通过uuid作为引用。

(3)使用ObjectLoader加载JSON模型

既然我们能够导出模型,肯定就可以导入。这里我们将使用到Three.js内置的对象THREE.ObjectLoader来加载模型:

//实例化ObjectLoader对象
var loader = new THREE.ObjectLoader();
//加载外部的JSON格式的模型,并在回调中将生成的模型对象添加到场景中
loader.load("./scene.json", function (group) {
    scene.add(group);
});

案例截图:

image-20191226202200653

完整代码如下:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 OrbitControls.js-->
    <script src="./OrbitControls.js"></script>

    <script>
        // 1、创建场景
        var scene = new THREE.Scene();

        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );

        // 2、设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);
        // 实例化相机插件对象
        var control = new THREE.OrbitControls(camera, renderer.domElement);

        //实例化ObjectLoader对象
        var loader = new THREE.ObjectLoader();
        //加载外部的JSON格式的模型,并在回调中将生成的模型对象添加到场景中
        loader.load("./scene.json", function (group) {
            scene.add(group);
        });

        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);
        // 10、实例化性能监视插件
        var stats = new Stats();
        // 10.1 把stats对象生成的dom,添加到页面中(这样就能在页面中看到性能监视器了)
        document.body.appendChild(stats.dom);
        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);

            // 10.2 更新性能插件
            stats.update();
            // 更新相机插件
            control.update();
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();
    </script>
</body>

</html>

注意:只要是通过 loader.load()方法导入,必须要使用VSCode编辑器中的 live-server插件的方式打开页面。

不然会有跨域的问题。

4.6、glTF格式文件的导出和导入

Three.js官方推荐我们使用的3D模型的格式为glTF,由于glTF专注于传输,因此它的传输和解析的速度都很快。glTF模型功能包括:网格、材质、纹理、灯光、相机等。

先在VSCode编辑器中安装glTF Tools插件,安装这个插件后我们就能在VSCode编辑器中查看 .gltf的文件效果了

image-20191226151720865

glTF格式的3D格式文件我们可以在sketchfab官网下载,这是一个国外比较知名的模型网站。

sketchfab官网模型下载地址:

sketchfab.com/3d-models?d…

image-20191228202759504

glTF格式加载器(loader)地址:

github.com/mrdoob/thre…

  1. 首先,将GLTFLoader加载器插件引入到页面。

    <!-- 引入 glTF加载器,用于加载glTF格式的3D模型 -->
    <script src="./gltfloader.js"></script>
    
  2. 然后创建一个加载器:

    // 实例化 GLTFLoader 对象
    var loader = new THREE.GLTFLoader();
    
  3. 使用加载器去加载模型,并调节一下模型大小在场景内展示:

    //加载外部的gltf格式的模型,并在回调中将生成的模型对象添加到场景中
    loader.load('./3D_gltf/scene.gltf', function (gltf) {
        // 设置缩放大小
        gltf.scene.scale.set(5, 5, 5);
        scene.add(gltf.scene);
    });
    

**注意:**以上的scene.gltf文件必须跟3D_gltf在同一个目录中,不能单独把scene.gltf移动到3D_gltf目录的外边。

image-20191226164955709

案例截图:

image-20191226202253723

完整代码如下:

<html>

<head>
    <title>Cube</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <!-- 引入three.js -->
    <script src="https://cdn.bootcss.com/three.js/92/three.js"></script>
    <!-- <script src="./three.js"></script> -->
    <!-- 引入 stats.js  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 OrbitControls.js-->
    <script src="./OrbitControls.js"></script>
    <!-- 引入 glTF加载器,用于加载glTF格式的3D模型 -->
    <script src="./gltfloader.js"></script>
    <script>
        // 1、创建场景
        var scene = new THREE.Scene();


        // 2、创建相机(透视投影相机)
        var camera = new THREE.PerspectiveCamera(
            50, // 相机视野
            window.innerWidth / window.innerHeight, // 水平方向和竖直方向长度的比值
            0.1, // 近端渲染距离
            1000 // 远端渲染距离
        );

        // 2设置相机位置简写方式:
        camera.position.set(5, 10, 10);
        // 3、创建渲染器
        var renderer = new THREE.WebGLRenderer();
        // 3.1 设置渲染器的大小(长宽)(设置渲染器为全屏)
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 3.2 将渲染结果展示到页面上
        document.body.appendChild(renderer.domElement);
        // 实例化相机插件对象
        var control = new THREE.OrbitControls(camera, renderer.domElement);

        // 实例化 GLTFLoader 对象
        var loader = new THREE.GLTFLoader();
        //加载外部的gltf格式的模型,并在回调中将生成的模型对象添加到场景中
        loader.load('./3D_gltf/scene.gltf', function (gltf) {
            // 设置缩放大小
            gltf.scene.scale.set(5, 5, 5);
            scene.add(gltf.scene);
        });

        // 7、创建光源
        var spotLight = new THREE.SpotLight(0xffffff);
        // 7.1 设置光源位置
        spotLight.position.set(0, 20, 20);
        // 7.2 设置光源照射的强度,默认值为 1
        spotLight.intensity = 5;
        // 7.3 将光源添加到场景中
        scene.add(spotLight);

        // 10、实例化性能监视插件
        var stats = new Stats();
        // 10.1 把stats对象生成的dom,添加到页面中(这样就能在页面中看到性能监视器了)
        document.body.appendChild(stats.dom);
        // 9、创建动画循环渲染函数
        function animate() {
            // 9.1 循环调用函数
            requestAnimationFrame(animate);

            // 10.2 更新性能插件
            stats.update();
            // 更新相机插件
            control.update();
            // 3.3 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
            renderer.setClearColor('rgb(135,206,250)', 1.0);
            renderer.render(scene, camera);
        };
        // 调用动画函数
        animate();
    </script>
</body>

</html>

5、Three.js性能优化建议

image-20191226202901179

5.1、尽量共用相同的几何体和材质

如果你需要创建三百个简单的相同颜色的立方体模型:

for (var i = 0; i < 300; i++) {
		var geometry = new THREE.BoxGeometry(10, 10, 10);
    var material = new THREE.MeshLambertMaterial({color: 0x00ffff});
    var cube = new THREE.Mesh(geometry, material);
  
  	// group(组的意思),通过组,方便管理,结构更加清晰。
  	var group = new THREE.Group();
    // group.add( cubeA );
  	// group.add( cubeB );
    group.add(cube);
}
// 把组,添加到场景中
scene.add( group );

我们尽量共用相同的几何体和材质:

var geometry = new THREE.BoxGeometry(10, 10, 10);
var material = new THREE.MeshLambertMaterial({color: 0x00ffff});
for (var i = 0; i < 300; i++) {
    var cube = new THREE.Mesh(geometry, material);
  
   	// group(组的意思),通过组,方便管理,结构更加清晰。
  	var group = new THREE.Group();
    // group.add( cubeA );
  	// group.add( cubeB );
    group.add(cube);
}
// 把组,添加到场景中
scene.add( group );

5.2、删除模型时,将材质和几何体从内存中清除

使用remove()将模型从场景内删除掉,大家会发现内存基本上没有怎么降低。因为几何体和材质还保存在内存当中,我们需要手动调用dispose()方法将其从内存中删除。

// 删除group,释放内存
function deleteGroup(name) {
    var group = scene.getObjectByName(name);
    if (!group) return;
    // 删除掉所有的模型组内的cube
    group.traverse(function (item) {
        if (item instanceof THREE.Mesh) {
            item.geometry.dispose(); //删除几何体
            item.material.dispose(); //删除材质
        }
    });
		
    scene.remove(group);
}

5.3、在循环渲染中避免使用更新

这里的更新指的是当前的几何体、材质、纹理等发生了修改,需要Three.js重新更新显存的数据,具体包括:

几何体:

geometry.verticesNeedUpdate = true; //顶点发生了修改
geometry.elementsNeedUpdate = true; //面发生了修改
geometry.morphTargetsNeedUpdate = true; //变形目标发生了修改
geometry.uvsNeedUpdate = true; //uv映射发生了修改
geometry.normalsNeedUpdate = true; //法向发生了修改
geometry.colorsNeedUpdate = true; //顶点颜色发生的修改

材质:

material.needsUpdate = true

纹理:

texture.needsUpdate = true;

如果它们发生更新,则将其设置为trueThree.js会通过判断,将数据重新传输到显存当中,并将配置项重新修改为false。这是一个很耗运行效率的过程,所以我们尽量只在需要的时候修改,不要放到render()方法当中循环设置。

5.4、只在需要的时候渲染

如果在没有操作的时候,让循环一直渲染,属于浪费资源,接下来我来带给大家一个只在需要时渲染的方法。

  1. 首先在循环渲染中加入一个判断,如果判断值为true时,才可以循环渲染:
var renderEnabled;
function animate() {

    if (renderEnabled) {
        renderer.render(scene, camera);
    }

    requestAnimationFrame(animate);
}

animate();

  1. 然后设置一个延迟器函数,每次调用后,可以将renderEnabled设置为true,并延迟三秒将其设置为false,这个延迟时间大家可以根据需求来修改:
//调用一次可以渲染三秒
var timeOut = null;
function timeRender() {
	//设置为可渲染状态
    renderEnabled = true;
    //清除上次的延迟器
    if (timeOut) {
        clearTimeout(timeOut);
    }

    timeOut = setTimeout(function () {
        renderEnabled = false;
    }, 3000);
}

  1. 接下来,我们在需要的时候调用这个timeRender()方法即可,比如在相机控制器更新后的回调中:
controls.addEventListener('change', function(){
    timeRender();
});

如果相机位置发生变化,就会触发回调,开启循环渲染,更新页面显示。

  1. 如果我们添加了一个模型到场景中,直接调用一下重新渲染即可:
scene.add(cube);
timeRender();

6、代码封装案例

代码封装:让代码复用、让代码更清晰、容易维护。

案例截图:

image-20191226201750263

案例目录结构

image-20191226201933262

index.html文件

<!DOCTYPE html>
<html>

<head>
    <meta charset=utf-8>
    <title>Three.js案例</title>
    <style>
        body {
            margin: 0;
        }

        canvas {
            width: 100%;
            height: 100%;
            display: block;
        }
    </style>
</head>

<body onload="init()">
    <!-- 引入 Three.js文件 -->
    <script src="https://cdn.bootcss.com/three.js/92/three.js"></script>
    <!-- 引入stats 性能检测插件  -->
    <script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
    <!-- 引入相机插件 -->
    <script src="./OrbitControls.js"></script>
    <!-- 引入我们自己写的代码文件 -->
    <script src="./index.js"></script>
</body>

</html>

index.js文件

//声明一些全局变量(场景、相机、渲染器、几何体、材质、网格)
var scene, camera, renderer, geometry, material, cube;

// 性能插件,监听fps
stats = new Stats();
document.body.appendChild(stats.dom);

//初始化渲染器
function initRenderer() {
    renderer = new THREE.WebGLRenderer(); //实例化渲染器
    renderer.setSize(window.innerWidth, window.innerHeight); //设置宽和高
    document.body.appendChild(renderer.domElement); //添加到dom
}

//初始化场景
function initScene() {
    scene = new THREE.Scene(); //实例化场景
}
// 初始化相机插件
function initControl() {
    control = new THREE.OrbitControls(camera, renderer.domElement);
}
//初始化相机
function initCamera() {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //实例化相机
    camera.position.set(0, 0, 15); // 设置相机的位置
}

//创建模型
function initMesh() {
    geometry = new THREE.BoxGeometry(2, 2, 2); //创建几何体
    material = new THREE.MeshNormalMaterial(); // 创建材质
    cube = new THREE.Mesh(geometry, material); //创建网格
    cube.position.set(2, 5, -6); // 给网格设置定位
    cube.scale.x = 2; //模型沿x轴放大一倍
    cube.scale.y = 0.5; //模型沿y轴缩小一倍
    cube.scale.z = 1; //模型沿z轴保持不变
    scene.add(cube); //将网格添加到场景
}

//运行动画
function animate() {
    requestAnimationFrame(animate); //循环调用函数
    cube.rotation.x += 0.01; //每帧网格模型的沿x轴旋转0.01弧度  半圈是180度
    cube.rotation.y += 0.02; //每帧网格模型的沿y轴旋转0.02弧度
    stats.update(); // 性能插件更新,监听fps
    control.update(); // 相机插件更新
    renderer.render(scene, camera); //渲染界面

}

//初始化函数,页面加载完成时调用
function init() {
    initRenderer(); // 渲染
    initScene(); // 场景
    initCamera(); // 相机
    initMesh(); // 物体
    initControl(); // 相机插件
    animate(); // 旋转,动画
}

7、模型拖拽案例

image-20191229093140729

1、引入相关文件和插件

<script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>  	
		<!-- 轨迹球插件作用:
      1. 鼠标按住左键可以旋转模型
      2. 鼠标滚轮可以缩放模型
		-->
    <script src="./js/TrackballControls.js"></script>
    <!--拖拽控件-->
    <script src="./js/DragControls.js"></script>
    <!--可视化平移控件-->
    <script src="./js/TransformControls.js"></script>

2、完整代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>拖拽控件</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="https://cdn.bootcss.com/three.js/r83/three.min.js"></script>
    <!-- 轨迹球插件 -->
    <script src="./js/TrackballControls.js"></script>
    <!--拖拽控件-->
    <script src="./js/DragControls.js"></script>
    <!--可视化平移控件-->
    <script src="./js/TransformControls.js"></script>
</head>

<body>
    <script>
        // 初始化场景、相机、渲染器、轨迹球控制器、灯光
        var scene, camera, renderer, controls, 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, 400, 600);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
        }

        // 渲染器
        function initRenderer() {
            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);
        }

        // 初始化模型
        function initContent() {
            // 创建坐标格辅助对象.
            var helper = new THREE.GridHelper(1200, 50, 0xCD3700, 0x4A4A4A);
            // 把坐标格添加到场景中去
            scene.add(helper);
            // 创建立方体
            var cubeGeometry = new THREE.BoxGeometry(100, 100, 100);
            // 创建法线网格材质
            var cubeMaterial = new THREE.MeshNormalMaterial();
            // 创建网格
            var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
            // 将网格添加到场景中
            scene.add(cube);
        }

        // 窗口变动触发的方法
        function onWindowResize() {
            // 设置相机方向
            camera.aspect = window.innerWidth / window.innerHeight;
            // 设置渲染器的渲染大小
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 初始化轨迹球控件
        function initControls() {
            // 创建轨迹球控制器
            controls = new THREE.TrackballControls(camera, renderer.domElement);
            //旋转速度 
            controls.rotateSpeed = 100;
            //变焦速度 
            controls.zoomSpeed = 3;
            //平移速度 
            controls.panSpeed = 100;
            //是否不变焦 
            controls.noZoom = false;
            //是否不平移 
            controls.noPan = false;
            //是否开启移动惯性 
            controls.staticMoving = true;
        }

        // 添加拖拽控件
        function initDragControls() {
            // 添加平移控件
            var transformControls = new THREE.TransformControls(camera, renderer.domElement);
            scene.add(transformControls);

            // 过滤不是 Mesh 的物体,例如辅助网格
            var objects = [];
            for (let i = 0; i < scene.children.length; i++) {
                if (scene.children[i].isMesh) {
                    objects.push(scene.children[i]);
                }
            }
            // 初始化拖拽控件
            var dragControls = new THREE.DragControls(objects, camera, renderer.domElement);

            // 鼠标略过
            dragControls.addEventListener('hoveron', function (event) {
                transformControls.attach(event.object);
            });
            // 开始拖拽
            dragControls.addEventListener('dragstart', function (event) {
                controls.enabled = false;
            });
            // 拖拽结束
            dragControls.addEventListener('dragend', function (event) {
                controls.enabled = true;
            });
        }

        // 初始化灯光
        function initLight() {
            light = new THREE.SpotLight(0xffffff);
            light.position.set(-300, 600, -400);
            scene.add(light);
        }

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

        function animate() {
            //更新轨迹球控件
            controls.update();
            requestAnimationFrame(animate);
            renderer.render(scene, camera);

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

</html>