【three.js】22. Imported Models导入模型

22. Imported Models导入模型

介绍

Three.js 可以让你创建很多原始几何体,但是当涉及到更复杂的形状时,我们最好使用专用的 3D 软件建模。
在本课中,我们将使用已经制作好的模型,但我们将在以后的课程中学习如何完全在 3D 软件中创建模型。

格式

随着时间的推移,已经出现了许多 3D 模型格式。每个3D格式都在给予大家解决方案,比如模型中嵌入了什么数据、权重、压缩、兼容性、版权等。
这就是为什么今天我们可以访问数百种模型格式:https://en.wikipedia.org/wiki/List_of_file_formats#3D_graphics。
有些3D格式专用于一种软件。一些已知非常轻,但有时缺乏具体数据。众所周知,有些存储库几乎包含您可能需要的所有数据,但它们很重。有些格式是开源的,有些格式不是,有些是二进制的,有些是 ASCII,等等。
如果您需要精确的数据并且找不到您的软件支持的适当格式,您甚至可以很容易地创建自己的格式。
以下是您可能会遇到的流行格式列表:

  • OBJ
  • FBX
  • STL
  • PLY
  • COLLADA
  • 3DS
  • GLTF

我们不会涵盖所有这些格式。这会很无聊,我们不需要这样做,因为已经有一种格式正在成为一种标准,应该可以满足您的大部分需求。

GLTF

GLTF 代表 GL 传输格式。它由 Khronos Group(OpenGL、WebGL、Vulkan、Collada 背后的人以及许多成员,如 AMD / ATI、Nvidia、Apple、id Software、Google、Nintendo 等)制作。
GLTF 在过去几年变得非常流行。
它支持不同的数据集。它可以包括几何体和材质等数据,也可以包括相机、灯光、场景图、动画、骨架、变形甚至多场景等数据。
它还支持各种文件格式,如 json、binary、embed textures。
GLTF 已成为实时标准。由于它正在成为一种标准,大多数 3D 软件、游戏引擎和库都要支持它。这意味着您可以在不同的环境中轻松获得相似的结果。
这并不意味着您必须在所有情况下都使用 GLTF。如果您只需要一个几何图形,您最好使用其他格式,如 OBJ、FBX、STL 或 PLY。你应该在每个项目上测试不同的格式,看看你是否拥有你需要的所有数据,文件是否太大,如果信息被压缩需要多长时间才能解压等等。

查找模型

首先,我们需要一个模型。正如我们之前所说,稍后我们将学习如何在 3D 软件中创建我们自己的模型,但现在,让我们使用预制模型。
GLTF 团队还提供各种模型,从简单的三角形到逼真的模型以及动画、变形、透明涂层材料等。
您可以在此存储库中找到它们:https://github.com/KhronosGroup/glTF-Sample-Models
如果你想测试这些模型,你必须下载或克隆整个存储库并获取你需要的文件。但我们将从一只简单的鸭子开始,您可以在/static/models/启动器的文件夹中找到它。

GLTF格式

虽然 GLTF 本身是一种格式,但它也可以有不同的文件格式。这有点复杂,但有充分的理由。
如果您打开该/static/models/Duck/文件夹,您将看到 4 个不同的文件夹。每个都包含鸭子,但 GLTF 格式不同:

  • glTF
  • glTF-Binary
  • glTF-Draco
  • glTF-Embedded

你甚至可以找到其他格式,但这 4 种是最重要的,涵盖了我们需要学习的内容。
当心; 您的操作系统可能会隐藏其中一些文件的扩展名。请参考代码编辑器中应显示扩展名的文件名。

glTF

这种格式是一种默认格式。该Duck.gltf文件是一个 JSON格式,您可以在编辑器中打开它。它包含各种信息,如相机、灯光、场景、材质、对象转换,但既不包含几何体也不包含纹理。该Duck0.bin文件是二进制文件,您无法打开阅读。它通常包含几何数据和与顶点相关的所有信息,如 UV 坐标、法线、顶点颜色等。DuckCM.png文件只是鸭子的纹理。
当我们加载这种格式时,我们只要加载Duck.gltf包含对其他文件的引用的文件,这些文件将被自动加载。

glTF-Binary

这种格式仅由一个文件组成。它包含我们在 glTF 默认格式中讨论的所有数据。那是一个二进制文件,您不能只在代码编辑器中打开它来查看里面的内容。
由于只有一个文件,因此这种格式可以更轻便且加载起来更舒适,但您将无法轻松更改其数据。例如,如果你想调整纹理大小或压缩纹理,你不能因为它在那个二进制文件中而与其他文件合并。

glTF-Draco

这种格式类似于glTF 默认格式,但缓冲区数据(通常是几何图形)是使用Draco 算法压缩的。如果比较.bin文件大小,您会发现它要轻得多。
虽然此格式有一个单独的文件夹,但您可以将 Draco 压缩应用于其他格式。
这个先放一边,以后再说。

glTF-Embedded

这种格式类似于glTF-Binary格式,因为它只有一个文件,但这个文件实际上是一个 JSON,您可以在编辑器中打开它。
这种格式的唯一好处是只有一个易于编辑的文件。

选择

选择正确的格式取决于您希望如何处理资源。
如果你想在导出后能够改变纹理或灯光的坐标,你最好选择glTF-default。它还具有分别加载不同文件的优势,从而提高了加载速度。
如果每个模型只需要一个文件并且不关心要不要修改资源,则最好选择glTF-Binary
在这两种情况下,您都必须决定是否要使用Draco压缩,但我们稍后会介绍这一部分。

设置

启动器由一个空平面组成。
因为 GLTF 是一个标准,它显然支持灯光。通常,当您将 GLTF 导入 Three.js 项目时,您最终会得到具有MeshStandardMaterial的网格,您可能还记得,如果您的场景中没有灯光,您将看不到太多这些材料。
启动器中已经有一个AmbientLight和一个DirectionalLight 。

在 Three.js 中加载模型

要在 Three.js 中加载 GLTF 文件,我们必须使用GLTFLoader。此类在THREE变量中默认不可用。我们需要从three位于examples/依赖项中的文件夹中导入它:

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'

然后我们可以像为 TextureLoader 一样实例化它:

/**
 * Models
 */
const gltfLoader = new GLTFLoader()

如果需要,我们也可以像在纹理课程中那样使用LoadingManager。
要加载模型,好消息,它几乎和加载纹理一样简单。我们调用该load(...)方法并使用正确的参数:

  • 文件的路径
  • 成功回调函数
  • 进度回调函数
  • 错误回调函数
gltfLoader.load(
    '/models/Duck/glTF/Duck.gltf',
    (gltf) =>
    {
        console.log('success')
        console.log(gltf)
    },
    (progress) =>
    {
        console.log('progress')
        console.log(progress)
    },
    (error) =>
    {
        console.log('error')
        console.log(error)
    }
)

您应该看到进度和正在调用的成功函数。如果无法加载文件,可能会调用错误函数。检查路径,不要忘记我们不能添加路径是/static的部分。

gltfLoader.load(
    '/models/Duck/glTF/Duck.gltf',
    (gltf) =>
    {
        console.log(gltf)
    }
)

将加载的模型添加到我们的场景中

如果查看控制台中记录的对象,您会发现很多元素。最重要的部分是scene属性,因为我们在导出的模型中只有一个场景。
scene包含了我们需要的一切。但它还包括更多。始终从研究其中可用的内容开始,并观察不同Groups、Object3D和Mesh的scale缩放属性。
我们得到这样的东西:

THREE.Group: scene
└───Array: children
    └───THREE.Object3D
        └───Array: children
            ├───THREE.PerspectiveCamera
            └───THREE.Mesh

mesh网格应该是我们的鸭子。我们并不真正关心PerspectiveCamera。相机和鸭子似乎都在场景的子数组中的第一个也是唯一一个Object3D中。更糟糕的是,Object3D已将scale设置为最小值。
正如您所看到的,即使是获取我们的鸭子也有点复杂,这是大多数初学者迷路的地方。
我们想要的只是让我们的鸭子出现在场景中。我们有多种方法可以做到这一点:

  • 将整体添加到我们的scene场景中。我们可以这样做,因为即使它的名字是scene,它实际上是一个Group。
  • scene的子项添加到我们的场景中并忽略未使用的PerspectiveCamera。
  • 在添加到场景之前过滤子项以删除不需要的对象,如PerspectiveCamera。
  • 仅添加网格,但最终得到的鸭子可能会被错误地缩放、定位或旋转。
  • 在 3D 软件中打开文件并删除PerspectiveCamera,然后再次导出GITF文件。

因为我们的模型结构简单,我们将Object3D添加到我们的场景中,而忽略里面未使用的PerspectiveCamera。在以后的课程中,我们会将整个场景添加为一个对象:

gltfLoader.load(
    '/models/Duck/glTF/Duck.gltf',
    (gltf) =>
    {
        scene.add(gltf.scene.children[0])
    }
)


你应该看到渲染了一只鸭子。
您可以尝试其他格式,但不能尝试尚不能使用的 Draco

gltfLoader.load(
    '/models/Duck/glTF/Duck.gltf', // Default glTF

// Or
gltfLoader.load(
    '/models/Duck/glTF-Binary/Duck.glb', // glTF-Binary

// Or
gltfLoader.load(
    '/models/Duck/glTF-Embedded/Duck.gltf', // glTF-Embedded

文件夹中提供了另一个名为FlightHelmet(也取自glTF 模型示例/static/models/)的模型。该模型只有一种格式,即默认的 glTF。
尝试加载此模型:

gltfLoader.load(
    '/models/FlightHelmet/glTF/FlightHelmet.gltf',
    (gltf) =>
    {
        scene.add(gltf.scene.children[0])
    }
)


我们没有得到漂亮的头盔,只渲染了几个零件。
问题是我们只将 loaded 的第一个child添加到我们的scene场景中。
我们可以尝试的是循环孩子并将他们添加到场景中:

for(const child of gltf.scene.children)
{
    scene.add(child)
}


这将产生更多元素,但不是全部。更糟糕的是,刷新时,您可能会得到不同的部分。
问题是当我们将一个child从一个场景添加到另一个场景时,它会自动从第一个场景中删除。这意味着现在第一个场景中的child更少了。
当我们添加第一个对象时,它会从第一个场景中移除,而第二个元素只是移动到第一个位置。但是您的循环现在采用数组的第二个元素。您将始终在children数组中保留元素。
这个问题有多种解决方案。第一个解决方案是获取已加载场景的第一个子节点并将其添加到我们的场景中,直到没有剩余为止:

while(gltf.scene.children.length)
{
    scene.add(gltf.scene.children[0])
}


我们现在得到了整个头盔。
另一种解决方案是复制children数组以获得一个未更改的独立数组。为此,我们可以使用扩展运算符...并将结果放入一个全新的数组中[]:

const children = [...gltf.scene.children]
for(const child of children)
{
    scene.add(child)
}

这是一种原生 JavaScript 技术,可以在不触及原始数组的情况下复制数组。
最后,我们之前提到的一个简单好用的解决方案是添加属性scene

scene.add(gltf.scene)

我们的头盔太小了,我们只能增加比例,但我们会回到我们的 Duck 并尝试使用 Draco 压缩版本。

Draco compression 压缩

让我们回到我们的鸭子,但这一次,我们将使用 Draco 版本:

gltfLoader.load(
    '/models/Duck/glTF-Draco/Duck.gltf',


可悲的是,我们没有得到任何鸭子。如果您查看日志,您应该会看到如下所示的警告No DRACOLoader instance provided。我们需要为我们的GLTFLoader提供一个DRACOLoader实例,以便它可以加载压缩文件。
image.png
正如我们在浏览文件时看到的,Draco 版本比默认版本要轻得多。压缩应用于缓冲区数据(通常是几何图形)。使用默认的 glTF、二进制 glTF或嵌入式 glTF并不重要。
它甚至不是 glTF 独有的,您可以将它与其他格式一起使用。但是 glTF 和 Draco 同时流行起来,所以 glTF 导出器的实现速度更快。
谷歌在开源 Apache 许可下开发算法:

  • 网站: https: //google.github.io/draco/
  • Git 存储库: https: //github.com/google/draco

添加 DRACOLoader

Three.js 已经支持 Draco。我们必须从DRACOLoader导入开始:

import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'

然后我们可以实例化加载器(在 gltfLoader 之前):

const dracoLoader = new DRACOLoader()

解码器在原生 JavaScript 和 Web Assembly (wasm) 中可用,并且它可以在 worker 中运行(我们在物理课结束时看到的另一个线程)。这两个功能显著提高了性能,但它们意味着具有完全独立的代码。
Three.js 已经提供了这个分离的代码。要找到它,我们必须浏览到 Three.js 依赖项并将 Draco 解码器文件夹复制到我们的/static/文件夹中。
这个 Draco 文件夹位于/node_modules/three/examples/js/libs/. 获取整个/draco/文件夹并将其复制到您的/static/文件夹中。我们现在可以将此文件夹的路径提供给我们的dracoLoader

dracoLoader.setDecoderPath('/draco/')

最后,我们可以使用setDRACOLoader(...)方法将DRACOLoader实例提供给GLTFLoader实例:

gltfLoader.setDRACOLoader(dracoLoader)


你的鸭子应该回来了,但这次是 Draco 压缩版本。
您仍然可以使用GLTFLoader加载未压缩的 glTF 文件,并且仅在需要时加载 Draco 解码器。

何时使用 Draco 压缩

虽然您可能认为 Draco 压缩是一个双赢的局面,但事实并非如此。是的,几何体更轻,但首先,您必须加载DRACOLoader类和解码器。其次,您的计算机需要花费时间和资源来解码压缩文件,这可能会导致体验开始时出现短暂的卡顿,即使我们使用的是 worker 和 Web Assembly 代码也是如此。
你必须适应并决定什么是最好的解决方案。如果你只有一个 100kB 几何模型,你可能不需要 Draco。但是,如果您有很大 MB 的模型要加载并且不关心用户体验开始时要进行一些等待,您可能需要 Draco 压缩。

动画

正如我们之前所说,glTF 也支持动画。Three.js 可以处理这些动画。

加载动画模型

首先,我们需要一个动画模型。我们可以使用位于文件夹中的狐狸/static/models/Fox/(也取自glTF 模型示例)。
更改加载该狐狸的路径:

gltfLoader.load(
    '/models/Fox/glTF/Fox.gltf',

tutieshi_640x400_5s.gif
我们渲染出现了问题; 狐狸太大了。如果您看不到它,请查看上方或缩小。
在处理动画之前,让我们修复比例。如果您查看导入场景的组成,狐狸由一个Object3D组成,它本身由一个Bone和一个SkinnedMesh组成。我不会解释它们是什么,但是我们不应该简单地缩放Object3D就解决问题了。即使现在可以用缩放解决,但是它可能不适用于更复杂的模型。
我们在这里可以做的是缩放加载的场景并将其直接添加到我们的场景中:

gltfLoader.load(
    '/models/Fox/glTF/Fox.gltf',
    (gltf) =>
    {
        gltf.scene.scale.set(0.025, 0.025, 0.025)
        scene.add(gltf.scene)
    }
)

处理动画

如果查看加载的对象,您会看到一个名为gltf包含多个AnimationClip 的animations属性。
image.png
这些AnimationClip不能轻易使用。我们首先需要创建一个AnimationMixer。AnimationMixer就像一个可以包含一个或多个AnimationClips 的对象相关联的播放器。这个想法是为每个需要动画的对象创建一个。
在 success 函数中,创建一个AnimationMixer混音器并发送gltf.sceneas 参数:

const mixer = new THREE.AnimationMixer(gltf.scene)

我们现在可以使用clipAction(...)该方法将AnimationClip添加到混合器中。让我们从第一个动画开始:

const action = mixer.clipAction(gltf.animations[0])

这个方法返回一个AnimationAction,我们终于可以调用play()它的方法了:

action.play()

遗憾的是,还是没有动画。
要播放动画,我们必须告诉混音器在每一帧更新自己。问题是我们的mixer变量已经在加载回调函数中声明了,我们在函数中无权访问它tick。为了解决这个问题,我们可以在加载回调函数之外声明一个mixer = null值的变量,并在加载模型时更新它:

let mixer = null

gltfLoader.load(
    '/models/Fox/glTF/Fox.gltf',
    (gltf) =>
    {
        gltf.scene.scale.set(0.03, 0.03, 0.03)
        scene.add(gltf.scene)

        mixer = new THREE.AnimationMixer(gltf.scene)
        const action = mixer.clipAction(gltf.animations[0])
        action.play()
    }
)

最后,我们可以用已经计算好的deltaTime 更新tick函数中的混音器。
但在更新它之前,我们必须测试mixer变量是否与null不同。这样,如果模型已加载,我们不会更新混音器,这意味着动画尚未准备好:

const tick = () =>
{
    // ...

    if(mixer)
    {
        mixer.update(deltaTime)
    }

    // ...
}

tutieshi_640x400_6s.gif
动画应该正在运行。您可以通过更改clipAction(...)方法中的值来测试其他动画。

const action = mixer.clipAction(gltf.animations[2])

tutieshi_640x400_4s.gif

Three.js在线编辑器

Three.js 拥有自己的在线编辑器。你可以在这里找到它: https: //threejs.org/editor/

它就像一个 3D 软件,但在线且功能较少。您可以创建图元、灯光、材质等。
因为您可以导入模型,所以这是测试您的模型是否正常工作的好方法。虽然要小心; 您只能测试由一个文件组成的模型。您可以尝试使用 glTF-Binary 或 glTF-Embedded 的文件格式。
将模型拖放到编辑器中。

你应该看到一只黑鸭子,因为没有光。从菜单中添加一个AmbientLight和一个DirectionalLight以更清楚地查看它。

最后,您可以以各种格式导出您的场景,您可以在您的代码中重复使用这些格式,但不在我们讨论范围内。
目前就是这样,但我们将在接下来的课程中多次使用加载的模型进行开发。

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

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

相关文章

强化学习中动作价值函数和状态价值函数的联系区别?

在强化学习中,动作价值函数(Q函数)和状态价值函数(V函数)都是值函数,用于评估在不同状态或状态动作对下的值。它们之间存在联系,但有一些区别: 动作价值函数(Q函数&#…

STM32CubeIDE基础学习-相关工程文件介绍

STM32CubeIDE基础学习-相关工程文件介绍 前言 保存的工程要大致了解熟悉里面的文件代表的是什么意思、干什么用的,这样才方便后面使用或移植代码等。 当成功创建工程后,打开基础工程保存路径后可以看到所有文件如下图所示: 如果工程越复杂&a…

DDR ECC的使用

DDR ECC的使用 DDR注入错误测试 DDR先刷一遍0,ECC_STATUS,ECC_ON_OF初始化为0,数据注入错误,写DDR,读DDR。 ECC_STATUS 该寄存器保存有关可纠正和不可纠正错误发生的信息。状态位独立地设置为1,表示每种错…

MySQL--优化(索引--聚簇和非聚簇索引)

MySQL–优化(索引–聚簇和非聚簇索引) 定位慢查询SQL执行计划索引 存储引擎索引底层数据结构聚簇和非聚簇索引索引创建原则索引失效场景 SQL优化经验 一、聚簇索引 聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点保存…

鸿蒙 自定义弹窗对CustomDialogController二次封装

前言: 鸿蒙官方提供了自定义customdialog,调用代码很臃肿,必须在当前页面创建customDialogController,否则无法正常弹窗dialog 解决方案:目前就定义了两种类型的dialog 具体代码如下: 1. 用于代理dialog的…

从安卓转战月薪6万的鸿蒙原来这么简单

近年来,各家大厂正在积极布局鸿蒙客户端开发,鸿蒙操作系统备受瞩目,不少安卓开发者纷纷转战鸿蒙,并取得了可观的经济回报。本文将为大家揭示,从安卓转战鸿蒙并获得月薪6万的简单之道,希望能给正在考虑转型的…

亿发解析:互联网浪潮席卷,新零售崛起成为未来十年无可忽视之势

随着人们消费能力和水平的提高,消费者对产品质量的关注已不再仅限于产品本身,而更加强调产品质量与消费服务体验的双重重要性。随着互联网、移动支付、快递物流等技术的发展,这些技术催生了零售领域的新模式、新经济和新业态,为新…

四信5G FWA家族再添猛将,让你一眼沦陷的5G CPE来了!

随着全球5G基础设施建设的日益完善,千行百业迎来巨大变革,越来越多的场景因为5G网络实现跨越式升级,人们生活早已离不开超高速的互联网连接。 5G FWA作为弥合光纤欠发达地区数字鸿沟挑战的“杀手级应用”,摆脱对传统有线网络基础设…

图形报表ECharts

1、 ECharts简介 ECharts缩写来自Enterprise Charts,商业级数据图表,是百度的一个开源的使用 JavaScript实现的数据可视化工具,可以流畅的运行在 PC 和移动设备上,兼容当前绝大 部分浏览器(IE8/9/10/11,Ch…

ubuntu下使用MATLAB过程中的若干问题

ubuntu版本:Ubuntu 20.04 内核:Linux 5.15.0-97-generic MATALB版本:MATLAB R2022b 问题1:运行脚本时闪退 报错信息: Inconsistency detected by ld.so: ../elf/dl-tls.c: 517: _dl_allocate_tls_init: Assertion l…

vue-cli项目因为webpack版本不兼容运行后报错

vue-cli项目运行后报错: Error: Rule can only have one resource source (provided resource and test include exclude) in {"exclude": [null],"use": [{"loader": "G:\\CustomerDay\\customerday\\node_modules\\cache-l…

制片管理工具:提高制片效率的必备工具

一、什么是制片管理工具 制片管理工具是一种为制片人提供支持和协助的软件或工具,并提供一种集中管理制作进度、任务分配、成本预算、资源管理和进度跟踪的方式。它可以帮助制片人在项目的开发、制作和发布方面更有效地进行规划和监督,确保整个流程能够…

redis-集群 原生部署和工具自动部署

什么redis集群? redis集群是一个提供在多个redis节点之间共享数据的程序集。它并不像redis主从复制模式那样仅提供一个master节点来提供写服务,而是会提供多个master节点来提供写服务,每个master节点中存储的数据都不一样,这些数据…

buildadmim生成代码时让菜单有层级

当我们使用buildadmin生成代码的时候,在菜单的部分, 有时希望它生的是一个带有层级的菜单,有时候则想生成一个没有层级的菜单 like this 经过本人测试 如果我们要生成没有层级的菜单 我们可以在高级设置中的 相对位置处更改,同时…

O2O:Adaptive policy learning for offline-to-online reinforcement learning

AAAI2023 paper Introduction 传统Online RL需要智能体与环境进行海量交互,而Offline RL容易受限于数据集质量。因此本文提出一种O2O的自适应策略学习框架APL。APL在离线阶段悲观更新策略而在现阶段乐观更新。进一步,基于框架分别提出value-based 以及…

蓝牙APP开发实现汽车遥控钥匙解锁汽车智能时代

在现代社会,随着科技的不断发展,汽车已经不再是简单的交通工具,而是与智能科技紧密相连的载体。其中,通过开发APP蓝牙程序实现汽车遥控钥匙成为了一种趋势,为车主带来了便捷与安全的体验。虎克技术公司作为行业领先者&…

Redis6 搭建主从集群架构

文章目录 搭建Redis主从集群架构1.集群结构2.准备实例和配置3.启动4.开启主从关系5.测试 搭建Redis主从集群架构 安装部署单机版Redis6可参考: 安装部署单机版Redis6 1.集群结构 我们搭建的主从集群结构如图: 我们计划是在一台虚拟机里去部署三个R…

区块链媒体套餐:精益求精链游媒体宣发推广7个关键细节分享-华媒舍

在如今竞争激烈的游戏行业,一款优秀的游戏缺乏有效的宣发推广,很难脱颖而出。而随着区块链技术的兴起,链游媒体的宣发推广成为游戏开发者和运营商的重要选择之一。本文将为大家介绍精益求精的链游媒体宣发推广的七个关键细节。 1. 定位目标受…

初学C++

注释 变量 作用:给一段指定的内存空间起名,方便操作这段内容 数据类型 变量名 变量初始值; 常量 用于记录程序中不可更改的数据 宏常量: #define 宏常量 常量值 const修饰的变量: const 数据类型 常量名 常量值; 关键字 …

从0到1入门C++编程——10 stack容器、queue容器、list容器、set容器、map容器

文章目录 一、stack容器二、queue容器三、list容器1、构造函数2、赋值和交换3、大小及判空4、插入和删除5、数据存取6、反转和排序7、排序案例 四、set/multiset容器1、构造和赋值2、大小和交换3、插入和删除4、查找和统计5、set和multiset的区别6、pair对组的创建7、排序及规则…