一、学习收获
1、Three.js 相机插件的使用
2、Three.js 基本元素 :精灵(Sprite)
3、Three.js 导出和导入JSON格式的3D模型
4、Three.js 导出和导入glTF格式的3D模型
5、Three.js 代码封装
二、主要内容:
1、Three.js中的相机插件
通过Three.js的相机控件OrbitControls.js可以对Threejs的三维场景进行缩放、平移、旋转操作,本质上改变的并不是场景,而是相机的参数,相机的位置角度不同,同一个场景的渲染效果是不一样,比如一个相机绕着一个场景旋转,就像场景旋转一样。OrbitControls.js是对Three.js中正交投影相机和透视投影相机对象进行了封装。
简言之:在Three.js中能够使用OrbitControls.js进行自由视角观察。
OrbitControls.js链接: github.com/mrdoob/thre…
使用OrbitControls.js
- 引入OrbitControls.js插件
<script src="./OrbitControls.js"></script>
- 实例化相机插件对象
var control = new THREE.OrbitControls(camera, renderer.domElement);
- 在每一帧执行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();
- 案例截图:
- 完整代码如下:
<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
添加一个3d
对象:
将一个网格添加到场景中进行使用
scene.add(cube);; //将网格添加到场景
删除一个3d对象
如果一个模型不再被使用到,需要彻底删除掉,我们可以使用remove
方法进行删除:
scene.remove(mesh); //将一个模型从场景中删除
3、Three.js内容补充
3.1、修改位置的3种方式
- 单独设置
camera.position.x = 5;
camera.position.y = 10;
camera.position.z = 10;
- 使用set方法,一次性设置所有
camera.position.set(5, 10, 10);
-
通过三维向量一次性设置
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种方式
- 单独设置
cube.scale.x = 2; //模型沿x轴放大一倍
cube.scale.y = 0.5; //模型沿y轴缩小一倍
cube.scale.z = 1; //模型沿z轴保持不变
- 使用set方法,一次性设置所有
cube.scale.set(2, 2, 2); //每个方向等比放大一倍
- 由于
scale
属性也是一个三维向量,通过三维向量一次性设置
cube.scale = new THREE.Vector3(2, 2, 2); //每个方向都放大一倍
3.3 修改模型转向(旋转)的3种方式
- 单独设置每个轴的旋转:
cube.rotation.x = Math.PI; //模型沿x旋转180度
cube.rotation.y = Math.PI * 2; //模型沿y轴旋转360度,跟没旋转一样的效果。。。
cube.rotation.z = - Math.PI / 2; //模型沿z轴逆时针旋转90度
- 使用
set
方法,一次性设置所有
cube.rotation.set(Math.PI, 0, - Math.PI / 2); //旋转效果和第一种显示的效果相同
-
通过重新赋值一个欧拉角对象来实现旋转调整【了解即可】
模型的
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对象,它是专门配合精灵的材质。注意:精灵没有阴影效果。
- 普通方式的精灵(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 案例截图:
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>
-
图片(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 案例截图:
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>
-
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 案例截图:
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)模型外观
以上代码创建粒子效果图:
粒子会吸附在几何体的表面
上面是一个通过球体几何体创建的一个最简单的粒子特效。
使用任何几何体都可以创建粒子的几何体,甚至自己生成的几何体都可以
比如创建星空案例:
// 创建跟几何体的相关属性的顶点(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
最常见的3D建模软件,广泛应用于广告、影视、工业设计、建筑设计、三维动画、多媒体制作、游戏、辅助教学以及工程可视化等领域。
SketchUp
SketchUp是一个极受欢迎并且易于使用的3D设计软件,官方网站将它比喻作电子设计中的“铅笔”。它的主要卖点就是使用简便,人人都可以快速上手。
4.2、常用3D模型素材网站:
sketchupbar
sketchfab
链接地址:sketchfab.com/
4.3、Three.js支持的3D模型格式
Three.js支持的导出格式
Three.js在线编辑器:threejs.org/editor/
Three.js支持的全部格式
4.4、在Three.js中导出3D模型步骤
(1)打开 Three.js在线编辑器
(2)点击添加按钮,选择将要添加的几何体模型
(3)设置几何体模型的材质类型和材质颜色
(4)设置几何体模型的属性(比如:位置、旋转、缩放)
(5)将3D模型导出(选择导出场景,导出的是一个json格式的文件)
json格式,一般用于Three.js官方的editor导出
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);
});
案例截图:
完整代码如下:
<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的文件效果了
glTF
格式的3D格式文件我们可以在sketchfab官网下载,这是一个国外比较知名的模型网站。
sketchfab官网模型下载地址:
glTF
格式加载器(loader)地址:
-
首先,将
GLTFLoader
加载器插件引入到页面。<!-- 引入 glTF加载器,用于加载glTF格式的3D模型 --> <script src="./gltfloader.js"></script>
-
然后创建一个加载器:
// 实例化 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); });
**注意:**以上的scene.gltf文件必须跟3D_gltf在同一个目录中,不能单独把scene.gltf移动到3D_gltf目录的外边。
案例截图:
完整代码如下:
<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性能优化建议
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;
如果它们发生更新,则将其设置为true
,Three.js
会通过判断,将数据重新传输到显存当中,并将配置项重新修改为false
。这是一个很耗运行效率的过程,所以我们尽量只在需要的时候修改,不要放到render()
方法当中循环设置。
5.4、只在需要的时候渲染
如果在没有操作的时候,让循环一直渲染,属于浪费资源,接下来我来带给大家一个只在需要时渲染的方法。
- 首先在循环渲染中加入一个判断,如果判断值为
true
时,才可以循环渲染:
var renderEnabled;
function animate() {
if (renderEnabled) {
renderer.render(scene, camera);
}
requestAnimationFrame(animate);
}
animate();
- 然后设置一个延迟器函数,每次调用后,可以将
renderEnabled
设置为true
,并延迟三秒将其设置为false
,这个延迟时间大家可以根据需求来修改:
//调用一次可以渲染三秒
var timeOut = null;
function timeRender() {
//设置为可渲染状态
renderEnabled = true;
//清除上次的延迟器
if (timeOut) {
clearTimeout(timeOut);
}
timeOut = setTimeout(function () {
renderEnabled = false;
}, 3000);
}
- 接下来,我们在需要的时候调用这个
timeRender()
方法即可,比如在相机控制器更新后的回调中:
controls.addEventListener('change', function(){
timeRender();
});
如果相机位置发生变化,就会触发回调,开启循环渲染,更新页面显示。
- 如果我们添加了一个模型到场景中,直接调用一下重新渲染即可:
scene.add(cube);
timeRender();
6、代码封装案例
代码封装:让代码复用、让代码更清晰、容易维护。
案例截图:
案例目录结构
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、模型拖拽案例
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>