相机,类似于眼睛,用于在3D舞台中,放置在不同的位置,实现通过不同的角度观察物体。
查看 Three.js 的文档,可以看到 Camera 是一个抽象类,一般不直接使用,其他类型的 Camera 实现了这个抽象类。有以下的几种相机
此处以透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)作为主要重点。
语法:PerspectiveCamera( fov, aspect, near, far );
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 2000 );
scene.add( camera );const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 2000 );
scene.add( camera );
// 相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(5, 5, 5);
// 同
camera.position.z = 5;
camera.position.x = 5;
camera.position.y = 5;// 相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(5, 5, 5);
// 同
camera.position.z = 5;
camera.position.x = 5;
camera.position.y = 5;
语法:OrthographicCamera( left, right, top, bottom, near, far )
const s = 5; // 假设一个范围
const k = window.innerWidth / window.innerHeight; // 视图的长宽比(canvas画布的长宽比)
const camera = new THREE.OrthographicCamera( -s*k, s*k, s , -s, 0.1,2000)
scene.add( camera );const s = 5; // 假设一个范围
const k = window.innerWidth / window.innerHeight; // 视图的长宽比(canvas画布的长宽比)
const camera = new THREE.OrthographicCamera( -s*k, s*k, s , -s, 0.1,2000)
scene.add( camera );
缓冲类型几何体:BufferGeometry
const geometry = new THREE.BufferGeometry();const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
0,0,0, // 第1个点的位置
3,0,0, // 第2个点的位置
0,3,0, // 第3个点的位置
0,0,3, // 第4个点的位置
// ... // 更多的点
])const vertices = new Float32Array([
0,0,0, // 第1个点的位置
3,0,0, // 第2个点的位置
0,3,0, // 第3个点的位置
0,0,3, // 第4个点的位置
// ... // 更多的点
])
// 通过 BufferAttribute来创建几何体的具体的顶点数据
const attribue = new THREE.BufferAttribute(vertices , 3); // 3 表示每3个数值为一组
// 设置缓冲几何体的顶点位置
geometry.attributes.position = attribue;// 通过 BufferAttribute来创建几何体的具体的顶点数据
const attribue = new THREE.BufferAttribute(vertices , 3); // 3 表示每3个数值为一组
// 设置缓冲几何体的顶点位置
geometry.attributes.position = attribue;
// 点模型的材料方法
const material = new THREE.PointsMaterial({
color:0xff6600,
size: 0.1, //设置点的大小
})// 点模型的材料方法
const material = new THREE.PointsMaterial({
color:0xff6600,
size: 0.1, //设置点的大小
})
// Mesh创建的是 网格模型 Points创建的就是 点模型
// 根据 点几何 和 点材料 创建 点模型
const point = new THREE.Points(geometry, material);
scene.add(point); // 把点模型 添加到场景中// Mesh创建的是 网格模型 Points创建的就是 点模型
// 根据 点几何 和 点材料 创建 点模型
const point = new THREE.Points(geometry, material);
scene.add(point); // 把点模型 添加到场景中
const geometry = new THREE.BufferGeometry();
// 创建几何体的 顶点数据
const vertices = new Float32Array([
0,0,0, // 第一个点的位置
3,0,0,
0,3,0,
0,0,3
])
// 通过 BufferAttribute来创建几何体的具体的顶点数据
const attribue = new THREE.BufferAttribute(vertices , 3);
geometry.attributes.position = attribue;const geometry = new THREE.BufferGeometry();
// 创建几何体的 顶点数据
const vertices = new Float32Array([
0,0,0, // 第一个点的位置
3,0,0,
0,3,0,
0,0,3
])
// 通过 BufferAttribute来创建几何体的具体的顶点数据
const attribue = new THREE.BufferAttribute(vertices , 3);
geometry.attributes.position = attribue;
// line模型的材料
const material = new THREE.LineBasicMaterial({
color:0xff6600,
linewidth:1,
linecap: 'round',
linejoin: 'round'
})// line模型的材料
const material = new THREE.LineBasicMaterial({
color:0xff6600,
linewidth:1,
linecap: 'round',
linejoin: 'round'
})
const line = new THREE.Line(geometry, material); // 普通的线const line = new THREE.Line(geometry, material); // 普通的线
const line = new THREE.LineLoop(geometry, material); // 闭合的线
const line = new THREE.LineSegments(geometry, material); // 非连续的线const line = new THREE.LineLoop(geometry, material); // 闭合的线
const line = new THREE.LineSegments(geometry, material); // 非连续的线
网格模型的父类都是 BufferGeometry ,内置的几何体( Box Plane 等等)本质上就是threejs计算好了之后的和封装相关属性的BufferGeometry几何体。
const geometry = new THREE.BoxGeometry(1,1,1, 点数量, 点数量, 点数量);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600,
wireframe:true, // 用线模型的模式展示mesh对应的三角形
})
const cube = new THREE.Mesh(geometry,material );
scene.add(cube);const geometry = new THREE.BoxGeometry(1,1,1, 点数量, 点数量, 点数量);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600,
wireframe:true, // 用线模型的模式展示mesh对应的三角形
})
const cube = new THREE.Mesh(geometry,material );
scene.add(cube);
geometry.scale(2,2,1);// 几何体在xyz三个方向上的缩放倍数geometry.scale(2,2,1);// 几何体在xyz三个方向上的缩放倍数
geometry.translate(3,1,1);
// 居中
// center方法 会让几何体居中对齐我们的坐标原点
// geometry.center();geometry.translate(3,1,1);
// 居中
// center方法 会让几何体居中对齐我们的坐标原点
// geometry.center();
geometry.rotateX(Math.PI/4);// x轴 8分之一圈的弧度
// geometry.rotateY( Math.PI / 4 ); // y轴
// geometry.rotateZ( Math.PI / 4 ); //z轴geometry.rotateX(Math.PI/4);// x轴 8分之一圈的弧度
// geometry.rotateY( Math.PI / 4 ); // y轴
// geometry.rotateZ( Math.PI / 4 ); //z轴
model.position.x = 2;
model.position.y = 2;
model.position.z = 2;
// 等价于
// model.position.set(2, 2, 2)
// 也可以利用模型的位移实现修改位置
// model.translateX(2);
// model.translateY(2);
// model.translateZ(2);model.position.x = 2;
model.position.y = 2;
model.position.z = 2;
// 等价于
// model.position.set(2, 2, 2)
// 也可以利用模型的位移实现修改位置
// model.translateX(2);
// model.translateY(2);
// model.translateZ(2);
model.scale.x = 0.5;
model.scale.y = 2;
model.scale.z = 2;model.scale.x = 0.5;
model.scale.y = 2;
model.scale.z = 2;
// 参数为弧度
model.rotateX(Math.PI / 4);
model.rotateY(Math.PI / 4);
model.rotateZ(Math.PI / 4);
// 等价于
// model.rotation.x = Math.PI / 4;
// model.rotation.y = Math.PI / 4;
// model.rotation.z = Math.PI / 4;// 参数为弧度
model.rotateX(Math.PI / 4);
model.rotateY(Math.PI / 4);
model.rotateZ(Math.PI / 4);
// 等价于
// model.rotation.x = Math.PI / 4;
// model.rotation.y = Math.PI / 4;
// model.rotation.z = Math.PI / 4;
material.color.set('#ff0000');
// 还可以有以下方式:
// 通过设置RGB的值来设置一个指定的颜色(这里采用的rgb值不是0-255 而是百分比)
// material.color.setRGB(221, 51, 51);
// 使用我们css的样式当中的常用的颜色的表达形式
// material.color.setStyle('#DD3333');
// 如果我们希望采用的是 0-255的数值来表达 RGB的值的话我们要采用下面的写法
// material.color.set('rgb(221,51,51)');material.color.set('#ff0000');
// 还可以有以下方式:
// 通过设置RGB的值来设置一个指定的颜色(这里采用的rgb值不是0-255 而是百分比)
// material.color.setRGB(221, 51, 51);
// 使用我们css的样式当中的常用的颜色的表达形式
// material.color.setStyle('#DD3333');
// 如果我们希望采用的是 0-255的数值来表达 RGB的值的话我们要采用下面的写法
// material.color.set('rgb(221,51,51)');
// 模型
console.log(model)
// 几何体
console.log(model.geometry)
// 材质
console.log(model.material)// 模型
console.log(model)
// 几何体
console.log(model.geometry)
// 材质
console.log(model.material)
const group = new THREE.Group();const group = new THREE.Group();
const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600
})
// 新建模型1
const model1 = new THREE.Mesh( geometry, material );
// 新建模型2
const model2 = new THREE.Mesh( geometry, material );
// model2其实是沿着x轴平移了2个单位(如果有分组的话 那么其实是相对于局部坐标移动)
model2.position.x = 3;
// 添加模型
group.add(model1);
group.add(model2);
// 也可以一次添加多个
// group.add(model1, model2);const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600
})
// 新建模型1
const model1 = new THREE.Mesh( geometry, material );
// 新建模型2
const model2 = new THREE.Mesh( geometry, material );
// model2其实是沿着x轴平移了2个单位(如果有分组的话 那么其实是相对于局部坐标移动)
model2.position.x = 3;
// 添加模型
group.add(model1);
group.add(model2);
// 也可以一次添加多个
// group.add(model1, model2);
// 分组本质上就多个小模型组成的一个大的模型 所以也可以调用模型对象的公共的方法
group.translateY(2);
group.translateX(2);// 分组本质上就多个小模型组成的一个大的模型 所以也可以调用模型对象的公共的方法
group.translateY(2);
group.translateX(2);
scene.getObjectByName()
方法,通过传入一个模型的name值,就可以快速的得到指定的模型对象const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600
})
const model1 = new THREE.Mesh( geometry, material);
const model2 = new THREE.Mesh( geometry, material);
model1.position.z = 2;
model2.position.z = 4;
model1.name = '1号';
model2.name = '2号';
// 创建一个分组 group
const group = new THREE.Group();
group.name = '某个组';
group.add(model1);
group.add(model2);
// 可以通过position来设置分组的位置
group.position.x = 3;
scene.add(group);
// 通过scene的children可以看到这个的场景的对象树的结构
console.log(scene.children);
// threejs当中 提供了有一个方法 来帮我们通过name属性来寻找指定的模型
const m = scene.getObjectByName('2号');
console.log('n', m)
m.rotateX( Math.PI / 4)const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600
})
const model1 = new THREE.Mesh( geometry, material);
const model2 = new THREE.Mesh( geometry, material);
model1.position.z = 2;
model2.position.z = 4;
model1.name = '1号';
model2.name = '2号';
// 创建一个分组 group
const group = new THREE.Group();
group.name = '某个组';
group.add(model1);
group.add(model2);
// 可以通过position来设置分组的位置
group.position.x = 3;
scene.add(group);
// 通过scene的children可以看到这个的场景的对象树的结构
console.log(scene.children);
// threejs当中 提供了有一个方法 来帮我们通过name属性来寻找指定的模型
const m = scene.getObjectByName('2号');
console.log('n', m)
m.rotateX( Math.PI / 4)
之前案例中我们使用的材料都是只是设置了颜色,实际开发中的3d模型呈现的却是更接近现实当中的各色各样的效果,此时就需要给模型文件使用纹理贴图的材料。
纹理贴图素材库
const textLoader = new THREE.TextureLoader(); // 创建纹理加载器const textLoader = new THREE.TextureLoader(); // 创建纹理加载器
const texture = textLoader.load('../floor.jpg'); // 通过加载一个图片来得到一个纹理const texture = textLoader.load('../floor.jpg'); // 通过加载一个图片来得到一个纹理
const material = new THREE.MeshBasicMaterial({
// 使用指定的纹理对象来渲染我们的模型
map: texture,
side: THREE.DoubleSide
})const material = new THREE.MeshBasicMaterial({
// 使用指定的纹理对象来渲染我们的模型
map: texture,
side: THREE.DoubleSide
})
side:THREE.DoubleSide
。表示双面可见,因为默认情况下几何体之后一面是贴图可见的const floor = new THREE.Mesh(geometry, material);
floor.rotateX( Math.PI /2 ); // 平放
scene.add(floor);const floor = new THREE.Mesh(geometry, material);
floor.rotateX( Math.PI /2 ); // 平放
scene.add(floor);
tips: 你可以创建其他的几何体模型来体验贴图的效果
// 设置阵列模式
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(5,5);//注意选择合适的阵列数量// 设置阵列模式
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(5,5);//注意选择合适的阵列数量
// 新建一个路牌
const lpgeometry = new THREE.PlaneGeometry(1,1);
const lpmaterial = new THREE.MeshBasicMaterial({
map: textLoader.load('../assets/指示牌.png'),
side: THREE.DoubleSide,
transparent: true, //如果需要保持贴图的透明性 需要开启透明属性
})
const lp = new THREE.Mesh( lpgeometry , lpmaterial);
lp.position.y = 2;
scene.add(lp);// 新建一个路牌
const lpgeometry = new THREE.PlaneGeometry(1,1);
const lpmaterial = new THREE.MeshBasicMaterial({
map: textLoader.load('../assets/指示牌.png'),
side: THREE.DoubleSide,
transparent: true, //如果需要保持贴图的透明性 需要开启透明属性
})
const lp = new THREE.Mesh( lpgeometry , lpmaterial);
lp.position.y = 2;
scene.add(lp);
transparent: true
我们经常在看到别人做的three.js项目中有很多的各式各样的3d模型,有车辆房子动物等
这些模型对于初学者来说,完全从0到1通过threejs的几何体去创建,其实是不太现实的
很多时候我们的3d模型都是使用 c4d 或者是 blender 这一类的3d建模设计软件去设计的
3d软件完成之后就可以导出对应的模型文件,而我们使用这些文件加载到我们的场景中就可以使用了
3d模型素材库
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader(); // 创建一个gltf文件加载器const loader = new GLTFLoader(); // 创建一个gltf文件加载器
loader.load('../assets/sofa/scene.gltf' , function(gltf){
const sofa = gltf.scene
// gltf的.scene就是我们最终可以添加到 场景当中的模型
gltf.scene.scale.x = 2;
gltf.scene.scale.y = 2;
gltf.scene.scale.z = 2;
sofa.position.x = 2
scene.add(gltf.scene)
})loader.load('../assets/sofa/scene.gltf' , function(gltf){
const sofa = gltf.scene
// gltf的.scene就是我们最终可以添加到 场景当中的模型
gltf.scene.scale.x = 2;
gltf.scene.scale.y = 2;
gltf.scene.scale.z = 2;
sofa.position.x = 2
scene.add(gltf.scene)
})
// 加载外部的 glb 格式的3d模型,加载的方式跟 gltf 格式的文件是一样的
loader.load('../assets/old_soviet_chair.glb', function(res){
const chair = res.scene;
// 默认出现在原点,所以需要进行一定的位置的移动
chair.position.set(-2,0,0);
scene.add( chair);
})// 加载外部的 glb 格式的3d模型,加载的方式跟 gltf 格式的文件是一样的
loader.load('../assets/old_soviet_chair.glb', function(res){
const chair = res.scene;
// 默认出现在原点,所以需要进行一定的位置的移动
chair.position.set(-2,0,0);
scene.add( chair);
})
z = 2;
sofa.position.x = 2
scene.add(gltf.scene)
})
- - 在threejs当中,大小没有单位,导入模型的大小,需要通过 scale 缩放进行大小的调整。
1. GLB文件的加载
- - glb是也是我们的threejs当中常用的3d模型文件,跟gltf的一个的区别在于,glb只有一个单独的文件,没有贴图文件的额外打包。不过它的使用流程跟gltf是一样的。
```javascript
// 加载外部的 glb 格式的3d模型,加载的方式跟 gltf 格式的文件是一样的
loader.load('../assets/old_soviet_chair.glb', function(res){
const chair = res.scene;
// 默认出现在原点,所以需要进行一定的位置的移动
chair.position.set(-2,0,0);
scene.add( chair);
})// 加载外部的 glb 格式的3d模型,加载的方式跟 gltf 格式的文件是一样的
loader.load('../assets/old_soviet_chair.glb', function(res){
const chair = res.scene;
// 默认出现在原点,所以需要进行一定的位置的移动
chair.position.set(-2,0,0);
scene.add( chair);
})