【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 = nullgltfLoader.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以更清楚地查看它。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计划任务与SSH远程登录

一、计划任务 一次性调度执行——at yum -y install at #安装at systemctl status atd # 查看启动状态 systemctl start atd # 启动服务 systemctl enable atd # 设置开机启动 at now 5min #5分钟后开始执行 at> 要执行的内容 at > <E…

整合shoir

​ 目录 一、📢前言 二、📝SpringBoot整合Shiro 2.1 📲导入依赖 org.springframework.boot spring-boot-starter-web <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><s…

【详识C语言】自定义类型之二:枚举

本章重点 枚举 枚举类型的定义 枚举的优点 枚举的使用 枚举 枚举顾名思义就是一一列举。 把可能的取值一一列举。 比如我们现实生活中&#xff1a; 一周的星期一到星期日是有限的7天&#xff0c;可以一一列举。 性别有&#xff1a;男、女、保密&#xff0c;也可以一一列举。…

vscode c/c++ 检测到 #include 错误。请更新 includePath。

问题背景 使用vscode打开项目后&#xff0c;头文件显示红色波浪线&#xff0c;没有引入。 检测到 #include 错误。请更新 includePath。已为此翻译单元(xxx)禁用波形曲线。 解决方法 gcc -v -E -x c - 显示所有头文件路径。 打开c_cpp_properties.json文件&#xff0c;粘贴…

【C++】类与对象(上篇)

一.类的引入 C与C语言比较起来&#xff0c;C引入了一个新的概念&#xff0c;叫做类。那么在C中&#xff0c;类又是什么呢&#xff1f; 在C中&#xff0c;类与C语言中的结构体相似&#xff0c;但不同的是&#xff0c;C中的类中&#xff0c;不仅可以定义变量&#xff0c;还能定义…

2024北京高端食品饮料博览会(5月)

2024北京高端食品饮料博览会&#xff08;5月&#xff09; 时间&#xff1a;2024年5月25-27日 地点&#xff1a;北京中国国际展览中心朝阳馆 主办单位&#xff1a;北京鸿利展览服务有限公司 承办单位&#xff1a;北京高端食品饮料博览会组委会 北京鸿利展览服务有限公司 展…

【pve】创建虚拟机

创建虚拟机 1.创建虚拟机-常规2.操作系统3. 系统4.磁盘5. cpu6.内存7.网络8.确认9.安装完成 1.创建虚拟机-常规 2.操作系统 这里选择自己的操作系统和系统的类别。 3. 系统 EFI存储选择自己磁盘 4.磁盘 调整适当的磁盘空间。 5. cpu 6.内存 根据自己的内存大小进行配…

【哈希】Leetcode 128. 最长连续序列 【中等】

最长连续序列 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。请你设计并实现时间复杂度为 O(n) 的算法解决此问题。示例 1&#xff1a;输入&#xff1a;nums [100,4,200,1,3,2]输出&#x…

最近开发中遇到的一些问题

puppeteer下载失败问题 使用的淘宝镜像&#xff0c;但执行命令npm i puppeteer之后&#xff0c;报错&#xff1a; npm ERR! code 1 npm ERR! path E:\项目-临时\test_install_puppeteer\node_modules\puppeteer npm ERR! command failed npm ERR! command C:\WINDOWS\system3…

cuda WSL2 无需单独安装

https://docs.nvidia.com/cuda/wsl-user-guide/index.html 这个写的很详细

TypeScript 哲学 - everyday Type

1、 2、TypeScript a structurally typed type system. 3、 type vs interface 3、literal reference 4、non-null assertion operator

微信小程序云开发教程——墨刀原型工具入门(Axure导入)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

快速上手:剧本杀dm预约平台小程序的制作流程

在当今的娱乐市场中&#xff0c;剧本杀已经成为一种备受欢迎的娱乐方式。为了给玩家提供更好的服务和体验&#xff0c;开发一个剧本杀DM预约平台小程序是至关重要的。下面&#xff0c;我们将详细介绍如何使用乔拓云第三方平台开发这样一个预约平台。 首先&#xff0c;打开乔拓云…

软件测试需求分析如何编写?为什么要进行测试需求分析?

在软件开发的过程中&#xff0c;软件测试需求分析是至关重要的一个环节。测试需求分析是指对待测软件的需求进行全面细致的分析&#xff0c;明确软件测试的目标和范围&#xff0c;为测试活动的进行提供指导。通过对软件需求的详细分析&#xff0c;可以确保测试人员清楚了解软件…

动手学深度学习-现代循环神经网络(GRU、LSTM、编码器-解码器等)

现代循环神经网络 上一章节&#xff08;循环神经网络&#xff09;介绍了循环神经网络的基础知识&#xff0c;这种网络可以更好的处理序列数据。我们在文本数据上实现了基于循环神经网络的语言模型&#xff0c;但是对于当今各种各样的序列学习问题&#xff0c;这些技术可能不够…

网络学习:Vlan间路由

目录 一、vlan间路由实现的方法 二、精确匹配转发&#xff08;交换机&#xff09;流程 三、最长匹配转发&#xff08;路由器&#xff09; 四、交换机最长匹配转发 五、总结 一、vlan间路由实现的方法 方法1&#xff1a;使用路由器的物理接口 特点&#xff1a;在路由器上…

Linux:kill进程

简介 kill 命令用于发送信号到进程。信号是操作系统用来通知进程特定事件的一种机制。 kill 命令的基本用法是&#xff1a; kill [信号] [进程ID]或者&#xff0c;您可以使用进程名称来代替进程ID&#xff1a; kill [信号] [进程名称]这里的 [信号] 是一个可选参数&#xf…

spring 事务失效的 12 种场景

文章目录 spring 事务失效的 12 种场景一、事务不生效1.访问权限问题2. 方法用 final 修饰3.方法内部调用&#xff08;自己玩自己&#xff09;3.1 新加一个 Service 方法3.2 在该 Service 类中注入自己3.3 通过 AopContent 类 4.Bean没有纳入Spring IOC容器管理5.多线程调用&am…

图像超分辨率:Fast Nearest Convolution for Real-Time Efficient Image Super-Resolution

9.Fast Nearest Convolution for Real-Time Efficient Image Super-Resolution 提出一种适用移动端的超分网络 一些tensor op 的推理时间 一些卷积结构的推理时间 网络结构NCNet 主干网络预测的是 残差&#xff0c;什么的残差&#xff1f; 是最近邻插值图像与 ground-truth的…

高性能服务系列【一】序言

偷得浮生半日闲&#xff0c;终于有心情把一些思考和经验整理成文字。个人工作经验主要在通讯和金融行业&#xff0c;包括股票、期货和债券。开发语言也以C/C为主&#xff0c;所以内容也主要集中在这些领域。 我不太喜欢和人动辄高谈阔论系统架构&#xff0c;架构设计本身就是以…