43:Three.js - 中

一、相机

  • 相机,类似于眼睛,用于在3D舞台中,放置在不同的位置,实现通过不同的角度观察物体。

  • 查看 Three.js 的文档,可以看到 Camera 是一个抽象类,一般不直接使用,其他类型的 Camera 实现了这个抽象类。有以下的几种相机

    • ArrayCamera:包含着一组子摄像机,常用于多人同屏的渲染,更好地提升VR场景的渲染性能
    • StereoCamera:双透视摄像机(立体相机),常用于创建 3D 立体影像,比如 3D 电影之类或 VR
    • CubeCamera:有6个渲染,分别是立方体的6个面,常用于渲染环境、反光等
    • OrthographicCamera:正交相机,在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变。这对于渲染2D场景或者UI元素是非常有用的。
    • PerspectiveCamera:透视相机,这一投影模式被用来模拟人眼所看到的景象,它是3D场景的渲染中使用得最普遍的投影模式。
  • 此处以透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)作为主要重点。

1. 创建透视相机

  • 语法:PerspectiveCamera( fov, aspect, near, far );

    • fov:摄像机视锥体垂直视野角度
    • aspect:摄像机视锥体长宽比,一般设置为Canvas画布宽高比width / height
    • near:摄像机视锥体近端面
    • far:摄像机视锥体远端面,far-near构成了视锥体高度方向
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 );

img

2. 相机位置.position

  • 生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs中虚拟相机同样如此。
  • 比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。
  • 相机对象Camera具有位置属性.position,通过位置属性.position可以设置相机的位置。
// 相机在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;

3. 创建正交相机

  • 语法:OrthographicCamera( left, right, top, bottom, near, far )

    • left:摄像机视锥体左侧面。
    • right:摄像机视锥体右侧面。
    • top:摄像机视锥体上侧面。
    • bottom:摄像机视锥体下侧面。
    • near:摄像机视锥体近端面。表示从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
    • far:摄像机视锥体远端面。表示距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值2000
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 );

img

4. 正交投影相机和透视投影相机区别

  1. 一句话描述:透视投影可以模拟人眼观察世界的视觉效果,正投影相机不会。
  2. 从视觉差异上来看,建筑物模型加载案例,就是用透视投影相机模拟人在空中俯视地面的效果,如果使用正投影相机渲染效果就不太自然。
  3. 对于大部分需要模拟人眼观察效果的场景,需要使用透视投影相机,比如人在场景中漫游,或是在高处俯瞰整个园区或工厂。
  4. 正交相机没有透视效果,也就是不会模拟人眼观察世界的效果。在一些不需要透视的场景你可以选择使用正投影相机,比如整体预览一个中国地图的效果,或者一个2D可视化的效果。

二、几何体(路径形式)

  • 缓冲类型几何体:BufferGeometry

    • 可以理解为绝大多数几何体的本质
    • 可以先简单的理解为通过定义顶点来描述几何体的外形。
    • 接下来我们就通过点模型、线模型和网格模型来逐步理解缓冲模型。

1. 点模型

  • 点模型就是在3d空间当中添加一个点。
  • 首先创建一个缓冲几何体
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;
  • 经过了这一步,缓冲几何体就有了具体的点的坐标属性
  • 点模型也有自己的材料方法:PointsMaterial
// 点模型的材料方法
const material = new THREE.PointsMaterial({
  color:0xff6600,
  size: 0.1, //设置点的大小
})// 点模型的材料方法
const material = new THREE.PointsMaterial({
  color:0xff6600,
  size: 0.1, //设置点的大小
})
  • 创建点模型的方法跟创建网格模型的方式基本是一样的,通过 THREE.Points 就可以创建点模型
// Mesh创建的是 网格模型  Points创建的就是 点模型
// 根据 点几何 和 点材料 创建 点模型
const point = new THREE.Points(geometry, material);
scene.add(point);  // 把点模型 添加到场景中// Mesh创建的是 网格模型  Points创建的就是 点模型
// 根据 点几何 和 点材料 创建 点模型
const point = new THREE.Points(geometry, material);
scene.add(point);  // 把点模型 添加到场景中
  • 场景中就有了我们刚才设置的三个点

img

2. 线模型

  • 线模型,就是在空间中设置一些连线。它的实现跟点模型有一定的联系,先有点才有线
  • 创建缓冲几何体,设置顶点数据 的流程是一样的
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;
  • 线模型也有自己的材料方法:LineBasicMaterial
// 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'
})
  • 使用 Line方法来创建 线模型
const line = new THREE.Line(geometry, material); // 普通的线const line = new THREE.Line(geometry, material); // 普通的线
  • 创建线模型还有其他的两个方法 LineLoop LineSegments
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); // 非连续的线

img

3. 网格模型

  • 经过了点模型和线模型的学习了解,我们再回来看一下我们最熟悉的 线网格模型。 我们在场景当中添加一个普通的 网格模型。
  • 在网格材质的地方我们设置一个新的属性 wireframe 为 true,wireframe的配置是开启展示模型的顶点和网线数据。

网格模型的父类都是 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);
  • 运行之后的效果

img

  • 可以看到 网格几何体的表面是由多个三角形组合而成。
  • 其实我们通过官方的 Geometry 的展示当中也可以看到基本所有的几何体都是由各个三角形组合而成。每个几何体基本都有一个 segments 效果的配置,这个值越大那么三角形细分的越多,就是表示就是几何体的表面越来越细节,越平滑,但是往往也会带来更大的性能的消耗。

img

  • 所以我们在一些3d模型的下载网站,在描述一个3d模型时会说明 三角形的数量和顶点数据的数量。

img

  • 所以不影响效果的前提下,我们尽量选择顶点数据更少的几何体。

4. 几何体的公共方法

  • threejs 对于所有的 BufferGeometry 的几何体都提供了一些公共的方法。
  1. 缩放
geometry.scale(2,2,1);// 几何体在xyz三个方向上的缩放倍数geometry.scale(2,2,1);// 几何体在xyz三个方向上的缩放倍数
  1. 位移
geometry.translate(3,1,1);
// 居中
// center方法 会让几何体居中对齐我们的坐标原点
// geometry.center();geometry.translate(3,1,1);
// 居中
// center方法 会让几何体居中对齐我们的坐标原点
// geometry.center();
  1. 旋转
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轴

三、模型和材质的公共方法

  1. 模型位置
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);
  1. 模型缩放
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;
  1. 模型旋转
// 参数为弧度
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;
  1. 材质颜色
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)');
  1. 模型身上的几何体和材质对象
// 模型
console.log(model)
// 几何体
console.log(model.geometry)
// 材质
console.log(model.material)// 模型
console.log(model)
// 几何体
console.log(model.geometry)
// 材质
console.log(model.material)

四、模型分组

  • 在进行中大型的3d场景开发时,往往有很多的几何体呈现在场景当中,但是有一些几何体又是有多个几何体构成的,比如说一辆车,可以简单理解为:一个盒形几何体 + 4个轮状几何体结合而成。在大的场景中我们希望把这个 1 + 4 的组合当成一个整体。这种场景我们就需要对于我们的模型进行分组。
  1. 新建分组
const group = new THREE.Group();const group = new THREE.Group();
  1. 添加模型
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);
  1. 分组操作
// 分组本质上就多个小模型组成的一个大的模型 所以也可以调用模型对象的公共的方法
group.translateY(2);
group.translateX(2);// 分组本质上就多个小模型组成的一个大的模型 所以也可以调用模型对象的公共的方法
group.translateY(2);
group.translateX(2);
  1. 命名
    • 当我们考虑使用分组意味着我们的场景当中放置的模型会越来越多,所以,如何查找指定模型,就会成为一个常见的需求
    • threejs提供了scene.getObjectByName() 方法,通过传入一个模型的name值,就可以快速的得到指定的模型对象
    • 所以我们一般会给我们的模型对象设置一个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模型呈现的却是更接近现实当中的各色各样的效果,此时就需要给模型文件使用纹理贴图的材料。

  • 纹理贴图素材库

    • https://texturelabs.org/
    • https://www.transparenttextures.com/
    • https://polyhaven.com/

1. 使用纹理贴图

  1. 创建纹理加载器
const textLoader = new THREE.TextureLoader(); // 创建纹理加载器const textLoader = new THREE.TextureLoader(); // 创建纹理加载器
  1. 加载一个图片来作为纹理对象
const texture = textLoader.load('../floor.jpg');  // 通过加载一个图片来得到一个纹理const texture = textLoader.load('../floor.jpg');  // 通过加载一个图片来得到一个纹理
  1. 创建材料
    • 创建材料时,设置的就不是color属性,而是使用map添加上一步创建好的纹理对象
const material = new THREE.MeshBasicMaterial({
  // 使用指定的纹理对象来渲染我们的模型
  map: texture,
  side: THREE.DoubleSide
})const material = new THREE.MeshBasicMaterial({
  // 使用指定的纹理对象来渲染我们的模型
  map: texture,
  side: THREE.DoubleSide
})
    • side:THREE.DoubleSide。表示双面可见,因为默认情况下几何体之后一面是贴图可见的
  1. 使用采用了贴图的材料
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: 你可以创建其他的几何体模型来体验贴图的效果

2. 贴图阵列(平铺)

  • 根据上面的代码运行的效果我们可以知道,默认情况下threejs会用一整张图去贴在材质的表面,但是有些时候我们需要的是在一个面上使用一个图片平铺展示的效果,比如说地毯瓷砖等。
  • 这就需要使用到阵列来实现。
// 设置阵列模式
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);//注意选择合适的阵列数量
  • 设置了阵列模式,那么贴图会按照指定的大小数量平铺在我们的模型上。

3. 透明贴图

  • 有些时候我们会在我们的场景当中使用写透明的贴图,比如说路牌指示,或者是标签效果等。这个时候使用plane几何体和透明图片的组合时候就会特别适合。
// 新建一个路牌
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
  • 否则 png 图片透明的部分就会显示为黑色。

六、加载外部的3d模型

  • 我们经常在看到别人做的three.js项目中有很多的各式各样的3d模型,有车辆房子动物等

  • 这些模型对于初学者来说,完全从0到1通过threejs的几何体去创建,其实是不太现实的

  • 很多时候我们的3d模型都是使用 c4d 或者是 blender 这一类的3d建模设计软件去设计的

  • 3d软件完成之后就可以导出对应的模型文件,而我们使用这些文件加载到我们的场景中就可以使用了

  • 3d模型素材库

    • https://sketchfab.com/
    • https://polyhaven.com/
    • http://gltfs.com/
  1. 引入gltf模型加载库 GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
  1. 新建一个 GLTF 加载器
const loader = new GLTFLoader(); // 创建一个gltf文件加载器const loader = new GLTFLoader(); // 创建一个gltf文件加载器
  1. 加载一个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)
})
    • 在threejs当中,大小没有单位,导入模型的大小,需要通过 scale 缩放进行大小的调整。
  1. GLB文件的加载
    • glb是也是我们的threejs当中常用的3d模型文件,跟gltf的一个的区别在于,glb只有一个单独的文件,没有贴图文件的额外打包。不过它的使用流程跟gltf是一样的。
// 加载外部的 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);
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/44267.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络安全】蜜罐部署实战DecoyMini攻击诱捕

蜜罐部署实战&DecoyMini攻击诱捕 前言一、蜜罐1. 概念2. 蜜罐溯源常见方式3. 蜜罐分类 二、蜜罐项目实战1. 配置DecoyMini1.1 命令行窗口运行1.2 修改配置信息 2. 登录DecoyMini3. 克隆网站3.1 增加仿真网站3.2 增加诱捕器3.3 查看端口监听3.4 克隆成功(蜜罐&am…

《我为什么要听你的 如何与强势的人相处》读书笔记二

目录 反驳例子 勇敢反击的益处 一些重要的自我保护法 不要再讲述你生活中的细节 别答应那些表意不明的请求 记录下一切 第一时间告知亲友 拒绝成为中间人 区别对待强势者 谨慎应对奉承话 想方设法快点跑 反驳例子 例子 例子: 例子: 例子&am…

Qt 类似vscode和matlab的分屏显示效果

运行截图 向右分屏 多分屏 全屏显示 介绍 实现了一个类似vscode和matlab的标签页显示分屏效果,支持鼠标拖拽分屏、全屏显示,可自适应调整大小,程序把要显示的Widget独立出来,可随时替换为其他的用户自定义Widget&#xff0c…

rust gtk 桌面应用 demo

《精通Rust》里介绍了 GTK框架的开发,这篇博客记录并扩展一下。rust 可以用于桌面应用开发,我还挺惊讶的,大学的时候也有学习过 VC,对桌面编程一直都很感兴趣,而且一直有一种妄念,总觉得自己能开发一款很好…

二、Java框架基础02 XML

二、XML 2.1 XML 简介 XML 即可扩展标记语言,一种简单的数据存储语言,使用一系列简单的标记来描述结构化数据 XML 的特点 XML 与操作系统,编程语言的开发平台无关规范统一,实现不同系统之间的数据交互 2.1.1 XML 的文档结构 以下…

[数学建模] [2019年A 模拟练习][层次分析法、熵值法、多目标优化、主成分分析法] 4. 深圳居民健康水平评估与测控模型研究

1、前言 2019年“深圳杯”数学建模挑战赛A题 原题,这个是当时学校内部校赛所作,为了拿到参加国赛名额,也权当是做一个简单的练手。 本次练习属于综合评判类,常用的方法无非 层次分析法、熵值法、多目标优化、主成分分析法 等&am…

引入第三方字体库 第三方字体库Google Fonts

googlefonts官方网站 googlefonts中国网站 本人是在微信小程序中引入 在static中建一个文件夹font-family 例如字体链接:https://fonts.font.im/css?familyKirangHaerang 将该链接的返回的资源的复制到css文件中 font-family.css /* [0] */ font-face {font-fam…

MURF20100CTR-ASEMI快恢复对管封装、尺寸、参数

编辑:ll MURF20100CTR-ASEMI快恢复对管封装、尺寸、参数 型号:MURF20100CTR 品牌:ASEMI 芯片个数:2 芯片尺寸:102MIL*2 封装:TO-220F 恢复时间:50ns 工作温度:-50C~150C 浪…

VSCode_常用插件_最新推荐

本文介绍前端开发领域常用的一些VSCode插件,插件是VSCode最重要的组成部分之一,本文列出了个人觉得是有用或有趣的一些插件。 一、代码管理相关插件 1、GitLens — Git supercharged 该插件增强了 VS Code 中的 Git,通过丰富的可视化和强…

Python 快速简单搭建HTTP本地服务器,内网通过浏览器访问

1 下载python https://www.python.org/downloads/ 2 安装python,安装时候选择把path加入电脑环境变量 3 由于python内建了简单http服务包,因此对于python来说,只需输入一行命令,就能轻松打开http服务。当然,要运行网页…

SQL-每日一题【619.只出现一次的最大数字】

题目 MyNumbers 表: 单一数字 是在 MyNumbers 表中只出现一次的数字。 请你编写一个 SQL 查询来报告最大的 单一数字 。如果不存在 单一数字 ,查询需报告 null 。 查询结果如下例所示。 示例 1: 示例 2: 解题思路 1.题目要求我…

CPU渲染or GPU渲染,你选对了吗?看完这六点就懂了!

在进行动画或效果图渲染时,选择适合的渲染方式对于项目的速度和质量至关重要。CPU渲染和GPU渲染作为两种主要的渲染方式,哪一种更适合你现在的情况?接下来我将从以下六个方面带大家深入了解,看完就知道怎么选了。 1.渲染原理 CPU…

gradio初体验

背景 近期随着很多开源大模型的出现,对于其如何落地,或者说充分地去挖掘其实际应用领域和商业价值变得格外重要。于是乎,对于不懂技术的前方市场或销售人员,如何在没有形成AI产品之前向其展示算法模型效果呢?这时候gr…

Spring 多数据源方法级别注解实现

Spring框架提供了多种数据源管理方式,其中多数据源管理是其中之一。多数据源管理允许应用程序使用多个数据源,而不是只使用一个数据源,从而提高了应用程序的灵活性和可靠性。 多数据源管理的主要目的是让应用程序能够在不同的数据库之间切换&…

【RabbitMQ(day1)】RabbitMQ的概述和安装

入门RabbitMQ 一、RabbitMQ的概述二、RabbitMQ的安装三、RabbitMQ管理命令行四、RabbitMQ的GUI界面 一、RabbitMQ的概述 MQ(Message Queue)翻译为消息队列,通过典型的【生产者】和【消费者】模型,生产者不断向消息队列中生产消息&…

SIGIR 2023 | 语音让对话推荐更easy,火山语音联合新加坡科学研究院发布业内首个语音对话推荐数据集

近年来,推荐系统在工业界取得了巨大成功,甚至成为互联网发展中不可或缺的增长引擎,基于此研究者们也在积极探索推荐系统的新形态,其中对话推荐系统(Conversational Recommender System,简称CRS)…

Mac上安装sshfs

目录 写在前面安装使用参考完 写在前面 1、本文内容 Mac上安装sshfs 2、平台 mac 3、转载请注明出处: https://blog.csdn.net/qq_41102371/article/details/130156287 安装 参考:https://ports.macports.org/port/sshfs/ 通过port安装 点击啊insta…

Qt/C++音视频开发49-多级连保存和推流设计(同时保存到多个文件/推流到多个平台)

一、前言 近期遇到个用户需要多级联的保存和推流,在ffmpegsave多线程保存类中实现这个功能,越简单越好,就是在推流的同时,能够开启自动转储功能,一边推流的同时一边录像保存到本地视频文件。最初设想的一个方案是new两…

【MySQL】之复合查询

【MySQL】之复合查询 基本查询多表查询笛卡尔积自连接子查询单行子查询多行子查询多列子查询在from子句中使用子查询 合并查询小练习 基本查询 查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J按照部门号升序而雇员的工资降序排序使用…

源码对接微软Azure OpenAI 规范注意点

众所周知,我们是访问不通OpenAI官方服务的,但是我们可以自己通过代理或者使用第三方代理访问接口 现在新出台的规定禁止使用境外的AI大模型接口对境内客户使用,所以我们需要使用国内的大模型接口 国内的效果真的很差,现在如果想合…