当前位置: 首页 > news >正文

three.js中怎么加载 .gltf/glb格式 文件

3D编辑器格式
用于特定应用程序(主要是3D编辑器):. .blend (Blender), .max (3d Studio Max), .mb and .ma (Maya), etc...
交换格式
有.OBJ, .DAE (Collada), .FBX.等格式。它们被设计出来用于3D编辑器之间交换信息的。因此,它们通常比所需的大得多(内含3D编辑器内所需要的信息)。
应用程序格式
用于特定的应用程序:游戏
传输格式
gLTF可能是第一个真正意义上的传输格式。我猜想VRML可能会被认为是第一个,但是VRML实际上是个相当糟的格式。
这是gLTF被设计出来擅长做的一些事情,其他格式没有的

体积小,适合用于传输
例如:这意味着大量它们的大数据量的数据,像顶点(vertices),被存为二进制格式。当你下载.gLTF格式文件后,可以将它零处理的传入GPU。这是它准备好的。相比较之下,VRML, .OBJ, or .DAE 这些格式 顶点(vertices)的存储和解析都是通过文本(text)。 文本存储的顶点位置(vertex positions)大约有二进制存储的3倍到5倍大。
易于被渲染(render)
与其他格式(尤其是应用程序格式)另一个不同是:glTF格式文件中的数据,意味着是用于渲染(render)的,而不是用于编辑的。对渲染不重要的那部分数据大都被删除了。多边形被转为三角形。材质有其应该有的值,可以在任何地方运行。

gLTF是被特别设计的。因此你应该能轻易下载glTF文件,显示它们,很少出麻烦。祝我们好运,真是这种情况,因为没有其他格式已经能够这样做。
我不确定我要展示什么。在某种程度上加载和显示gLTF文件比.OBJ文件要简单。不像.OBJ文件,材质是格式的一部分。我想我需要至少加载一个.OBJ文件,我遇到的问题可能会提供一些良好的信息
在网上搜索我发现 这个低模城市

v2-66b37756b062a69679bca7cce8fa4c92_720w

an example from the .OBJ article从这里开始,我删除了加载.OBJ的代码 ,替换为加载.gLTF的
旧.OBJ代码

const mtlLoader = new MTLLoader();
mtlLoader.loadMtl('resources/models/windmill/windmill-fixed.mtl', (mtl) => {
mtl.preload();
mtl.materials.Material.side = THREE.DoubleSide;
objLoader.setMaterials(mtl);
objLoader.load('resources/models/windmill/windmill.obj', (event) => {
const root = event.detail.loaderRootNode;
scene.add(root);
...
});
});

新.GLTF代码

{
const gltfLoader = new GLTFLoader();
const url = 'resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf';
gltfLoader.load(url, (gltf) => {
const root = gltf.scene;
scene.add(root);
...
});

我像以前一样保持自动框架代码
我们需要包含 GLTFLoader 并删除 OBJLoader.

import {LoaderSupport} from 'three/addons/loaders/LoaderSupport.js';
import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';
import {MTLLoader} from 'three/addons/loaders/MTLLoader.js';
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';

像这样运行

点击这里在其他窗口打开

神奇吧!它工作了,纹理和其他都工作了。
然后,我想看看我能否给汽车添加绕圈动画(animate),我得检查场景有没有这些汽车的分离的实体,还得检查场景有没有一条路可以用。
我写了一些代码将场景图(scenegraph)打印到 JavaScript console.
打印场景图(scenegraph)代码

function dumpObject(obj, lines = [], isLast = true, prefix = '') {
const localPrefix = isLast ? '└─' : '├─';
lines.push(${prefix}${prefix ? localPrefix : ''}${obj.name || 'no-name'} [${obj.type}]);
const newPrefix = prefix + (isLast ? ' ' : '│ ');
const lastNdx = obj.children.length - 1;
obj.children.forEach((child, ndx) => {
const isLast = ndx === lastNdx;
dumpObject(child, lines, isLast, newPrefix);
});
return lines;
}

我将在场景加载完时调用

const gltfLoader = new GLTFLoader();
gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {
const root = gltf.scene;
scene.add(root);
console.log(dumpObject(root).join('\n'));

Running that 获得清单

OSG_Scene [Scene]
└─RootNode_(gltf_orientation_matrix) [Object3D]
└─RootNode_(model_correction_matrix) [Object3D]
└─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]
└─RootNode [Object3D]
│ ...
├─Cars [Object3D]
│ ├─CAR_03_1 [Object3D]
│ │ └─CAR_03_1_World_ap_0 [Mesh]
│ ├─CAR_03 [Object3D]
│ │ └─CAR_03_World_ap_0 [Mesh]
│ ├─Car_04 [Object3D]
│ │ └─Car_04_World_ap_0 [Mesh]
│ ├─CAR_03_2 [Object3D]
│ │ └─CAR_03_2_World_ap_0 [Mesh]
│ ├─Car_04_1 [Object3D]
│ │ └─Car_04_1_World_ap_0 [Mesh]
│ ├─Car_04_2 [Object3D]
│ │ └─Car_04_2_World_ap_0 [Mesh]
│ ├─Car_04_3 [Object3D]
│ │ └─Car_04_3_World_ap_0 [Mesh]
│ ├─Car_04_4 [Object3D]
│ │ └─Car_04_4_World_ap_0 [Mesh]
│ ├─Car_08_4 [Object3D]
│ │ └─Car_08_4_World_ap8_0 [Mesh]
│ ├─Car_08_3 [Object3D]
│ │ └─Car_08_3_World_ap9_0 [Mesh]
│ ├─Car_04_1_2 [Object3D]
│ │ └─Car_04_1_2_World_ap_0 [Mesh]
│ ├─Car_08_2 [Object3D]
│ │ └─Car_08_2_World_ap11_0 [Mesh]
│ ├─CAR_03_1_2 [Object3D]
│ │ └─CAR_03_1_2_World_ap_0 [Mesh]
│ ├─CAR_03_2_2 [Object3D]
│ │ └─CAR_03_2_2_World_ap_0 [Mesh]
│ ├─Car_04_2_2 [Object3D]
│ │ └─Car_04_2_2_World_ap_0 [Mesh]
...

从这里我们可以看到所有的汽车,都在它们父节点"Cars"中

├─Cars [Object3D]
│ ├─CAR_03_1 [Object3D]
│ │ └─CAR_03_1_World_ap_0 [Mesh]
│ ├─CAR_03 [Object3D]
│ │ └─CAR_03_World_ap_0 [Mesh]
│ ├─Car_04 [Object3D]
│ │ └─Car_04_World_ap_0 [Mesh]

做一个简单的测试,我尝试将这些"Cars"节点的子节点绕它们的Y轴旋转。
在加载场景后,我查找了“Cars”节点并保存了结果。

let cars;
{
const gltfLoader = new GLTFLoader();
gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {
const root = gltf.scene;
scene.add(root);
cars = root.getObjectByName('Cars');

然后在render render函数中我们可以设置carsCars每个子节点旋转。

function render(time) {
time *= 0.001; // convert to seconds

if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}

if (cars) {
for (const car of cars.children) {
car.rotation.y = time;
}
}

renderer.render(scene, camera);

requestAnimationFrame(render);
}

于是我们得到

click here to open in a separate window

呃~,看起来很不幸,这场景没有设计汽车动画,因为他们当初不是用于此目的的。这卡车在错误的方向旋转。
这引出一个很重要的点,当你把东西做成3D时,你需要预先计划并设计你的资源(assets),让它们拥有能正确使用的初始功能。所以它们应该有正确的比例...
因为我不是艺术家,我不懂blender,我只能hack这个例子。我们将把每辆车,和它们的父节点,放到另一个 Object3D. 我们将移动这些 Object3D 的对象去移动车,但是我们可以设置汽车的原始Object3D 重新定位它,所以它是关于我们真正需要的地方。
回顾下场景图(scene graph)看起来只有三种车,Car_08", "CAR_03", and "Car_04"。每种汽车都会使用相同的调整。
我给每个汽车都写了代码,将它们的父节点换成新的 Object3D, 将新 Object3D 添加到场景, 应用于每种 类型的车的设置,修正它的朝向。 增加新的 Object3D a cars array.

let cars;
const cars = [];
{
const gltfLoader = new GLTFLoader();
gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {
const root = gltf.scene;
scene.add(root);

cars = root.getObjectByName('Cars');

const loadedCars = root.getObjectByName('Cars');
const fixes = [
{ prefix: 'Car_08', rot: [Math.PI * .5, 0, Math.PI * .5], },
{ prefix: 'CAR_03', rot: [0, Math.PI, 0], },
{ prefix: 'Car_04', rot: [0, Math.PI, 0], },
];

root.updateMatrixWorld();

for (const car of loadedCars.children.slice()) {
const fix = fixes.find(fix => car.name.startsWith(fix.prefix));
const obj = new THREE.Object3D();
car.getWorldPosition(obj.position);
car.position.set(0, 0, 0);
car.rotation.set(...fix.rot);
obj.add(car);
scene.add(obj);
cars.push(obj);
}
...

这些修正朝向的车

click here to open in a separate window

现在让我们驾驶着它们绕圈
制作一个简单的驾驶系统对这篇文章来说太多了,但作为代替我们可以做一个基于所有道路做一个弯曲路径,然后放置车到路上。下面的图是blender创建的路径。

上传失败

我需要一种方式在Blender之外花去路径数据的方法 幸运的是,我能选择我的路径并通过"write nurbs"导出.OBJ

添加图片注释,不超过 140 字(可选)

打开.OBJ文件我能够获取点列表,我格式化后

const controlPoints = [
[1.118281, 5.115846, -3.681386],
[3.948875, 5.115846, -3.641834],
[3.960072, 5.115846, -0.240352],
[3.985447, 5.115846, 4.585005],
[-3.793631, 5.115846, 4.585006],
[-3.826839, 5.115846, -14.736200],
[-14.542292, 5.115846, -14.765865],
[-14.520929, 5.115846, -3.627002],
[-5.452815, 5.115846, -3.634418],
[-5.467251, 5.115846, 4.549161],
[-13.266233, 5.115846, 4.567083],
[-13.250067, 5.115846, -13.499271],
[4.081842, 5.115846, -13.435463],
[4.125436, 5.115846, -5.334928],
[-14.521364, 5.115846, -5.239871],
[-14.510466, 5.115846, 5.486727],
[5.745666, 5.115846, 5.510492],
[5.787942, 5.115846, -14.728308],
[-5.423720, 5.115846, -14.761919],
[-5.373599, 5.115846, -3.704133],
[1.004861, 5.115846, -3.641834],
];

THREE.js有几个曲线(curve)的类。 CatmullRomCurve3 那种曲线的事情是尝试通过这些点进行平滑的曲线
事实上直接提供这些点会生成这样的曲线

添加图片注释,不超过 140 字(可选)

但是,我想要更尖锐的转角。如果我们计算了一些额外的点,我们可以得到我们想要的东西。 每两对点之间,我们需要在两点之间的10%处和90%处计算额外的两个点,并将它们传递给 CatmullRomCurve3.
这会得到这样的曲线

添加图片注释,不超过 140 字(可选)

曲线代码

let curve;
let curveObject;
{
const controlPoints = [
[1.118281, 5.115846, -3.681386],
[3.948875, 5.115846, -3.641834],
[3.960072, 5.115846, -0.240352],
[3.985447, 5.115846, 4.585005],
[-3.793631, 5.115846, 4.585006],
[-3.826839, 5.115846, -14.736200],
[-14.542292, 5.115846, -14.765865],
[-14.520929, 5.115846, -3.627002],
[-5.452815, 5.115846, -3.634418],
[-5.467251, 5.115846, 4.549161],
[-13.266233, 5.115846, 4.567083],
[-13.250067, 5.115846, -13.499271],
[4.081842, 5.115846, -13.435463],
[4.125436, 5.115846, -5.334928],
[-14.521364, 5.115846, -5.239871],
[-14.510466, 5.115846, 5.486727],
[5.745666, 5.115846, 5.510492],
[5.787942, 5.115846, -14.728308],
[-5.423720, 5.115846, -14.761919],
[-5.373599, 5.115846, -3.704133],
[1.004861, 5.115846, -3.641834],
];
const p0 = new THREE.Vector3();
const p1 = new THREE.Vector3();
curve = new THREE.CatmullRomCurve3(
controlPoints.map((p, ndx) => {
p0.set(...p);
p1.set(...controlPoints[(ndx + 1) % controlPoints.length]);
return [
(new THREE.Vector3()).copy(p0),
(new THREE.Vector3()).lerpVectors(p0, p1, 0.1),
(new THREE.Vector3()).lerpVectors(p0, p1, 0.9),
];
}).flat(),
true,
);
{
const points = curve.getPoints(250);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({color: 0xff0000});
curveObject = new THREE.Line(geometry, material);
scene.add(curveObject);
}
}

代码第一部分是创建了曲线。 代码第二部分是从曲线生成了250个点,然后创建了一个对象来显示这些点连起来的线。
运行 the example 我没有看见曲线。 为了让它可见我关闭了深度测试,并最后渲染它

curveObject = new THREE.Line(geometry, material);
material.depthTest = false;
curveObject.renderOrder = 1;

我发现路线实在太小了

上传失败

检查Blender中的层次结构我发现艺术家缩放了节点所有汽车。

上传失败

缩放对于实时3D应用程序是十分糟糕的。这会到这各种各样的问题,并且会以无休止的沮丧结束。 艺术家经常不知道这些,因为在3D编辑程序中缩放整个场景是非常容易的。 但是,如果你决定要做实时3D应用程序,我建议要求你的艺术家/设计人员不要缩放任何东西。 如果他们修改了scale,他们必须找到一种方式应用于缩放后的顶点。这样你就能,在导入程序后不用管缩放了。
还有, 不仅是缩放, 在这个例子中,车辆是通过父节点缩放,偏移的。 Cars 节点. 这将让在世界空间中实时移动车辆变得非常困难。 清楚地说,这个例子中,我们能希望车辆绕着世界空间开,这就会带来问题。 如果有些东西地数据处理在局部空间,如月球绕着地球转,就很少带来问题。
回到我写地打印场景图地方法。 让我们每个节点打印 位置, 旋转, 缩放 。

function dumpVec3(v3, precision = 3) {
return ${v3.x.toFixed(precision)}, ${v3.y.toFixed(precision)}, ${v3.z.toFixed(precision)};
}

function dumpObject(obj, lines, isLast = true, prefix = '') {
const localPrefix = isLast ? '└─' : '├─';
lines.push(${prefix}${prefix ? localPrefix : ''}${obj.name || 'no-name'} [${obj.type}]);
const dataPrefix = obj.children.length
? (isLast ? ' │ ' : '│ │ ')
: (isLast ? ' ' : '│ ');
lines.push(${prefix}${dataPrefix} pos: ${dumpVec3(obj.position)});
lines.push(${prefix}${dataPrefix} rot: ${dumpVec3(obj.rotation)});
lines.push(${prefix}${dataPrefix} scl: ${dumpVec3(obj.scale)});
const newPrefix = prefix + (isLast ? ' ' : '│ ');
const lastNdx = obj.children.length - 1;
obj.children.forEach((child, ndx) => {
const isLast = ndx === lastNdx;
dumpObject(child, lines, isLast, newPrefix);
});
return lines;
}

运行的结果

OSG_Scene [Scene]
│ pos: 0.000, 0.000, 0.000
│ rot: 0.000, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─RootNode_(gltf_orientation_matrix) [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: -1.571, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─RootNode_(model_correction_matrix) [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: 0.000, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: 1.571, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─RootNode [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: 0.000, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
├─Cars [Object3D]
│ │ pos: -369.069, -90.704, -920.159
│ │ rot: 0.000, 0.000, 0.000
│ │ scl: 1.000, 1.000, 1.000
│ ├─CAR_03_1 [Object3D]
│ │ │ pos: 22.131, 14.663, -475.071
│ │ │ rot: -3.142, 0.732, 3.142
│ │ │ scl: 1.500, 1.500, 1.500
│ │ └─CAR_03_1_World_ap_0 [Mesh]
│ │ pos: 0.000, 0.000, 0.000
│ │ rot: 0.000, 0.000, 0.000
│ │ scl: 1.000, 1.000, 1.000

这向我们展示了原始场景中的 Cars 已移除其旋转和缩放并应用于其子节点。 这表明用于创建 .GLTF 文件的导出器在这里做了一些特殊的处理,或者艺术家更可能导出了与相应 .blend 文件不同的文件版本,这就是它们不匹配的原因。
这意味着,我可能需要亲自下载这个.blend文件并导出它。导出它之前,我需要检查所有主要节点,并移除它的任何变换。
顶部的所有节点

OSG_Scene [Scene]
│ pos: 0.000, 0.000, 0.000
│ rot: 0.000, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─RootNode_(gltf_orientation_matrix) [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: -1.571, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─RootNode_(model_correction_matrix) [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: 0.000, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000
└─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]
│ pos: 0.000, 0.000, 0.000
│ rot: 1.571, 0.000, 0.000
│ scl: 1.000, 1.000, 1.000

也是浪费。
理想情况下,场景将由一个没有位置、旋转或缩放的“根”节点组成。 在运行时,我可以将所有子节点从该根中拉出,并将它们作为场景本身的父级。 可能有像“Cars”这样的根的子节点,它可以帮助我找到所有的汽车,但理想情况下它也没有平移、旋转或缩放,因此我可以用最少的工作将汽车重新设置为场景。
在任何情况下,最快速的(虽然可能不是最好的)修复,就是只调整用于显示曲线的对象。
这是我完成后的
首先,我调整了曲线的位置,然后发现了可以正常显示的数值,接着我把它隐藏了。

{
const points = curve.getPoints(250);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({color: 0xff0000});
curveObject = new THREE.Line(geometry, material);
curveObject.scale.set(100, 100, 100);
curveObject.position.y = -621;
curveObject.visible = false;
material.depthTest = false;
curveObject.renderOrder = 1;
scene.add(curveObject);
}

然后,我写了代码来沿着曲线移动汽车。 给每辆车选取曲线上的从0到1的位置,用 curveObject对象变换计算出一个世界空间的点 接下来,我们选取曲线远一点的点。 使用lookAt 设置汽车的朝向 并将汽车放置在两点重点。

// 创建两个向量用于路径计算
const carPosition = new THREE.Vector3();
const carTarget = new THREE.Vector3();

function render(time) {
...

for (const car of cars) {
car.rotation.y = time;
}

{
const pathTime = time * .01;
const targetOffset = 0.01;
cars.forEach((car, ndx) => {
// 一个介于 0 和 1 之间的数字,用于均匀间隔汽车
const u = pathTime + ndx / cars.length;

// 获取第一个点
curve.getPointAt(u % 1, carPosition);
carPosition.applyMatrix4(curveObject.matrixWorld);

// 曲线再远点获取第二个点
curve.getPointAt((u + targetOffset) % 1, carTarget);
carTarget.applyMatrix4(curveObject.matrixWorld);

// 把汽车放置在第一个点 (暂时的)
car.position.copy(carPosition);
// 汽车的第二个点
car.lookAt(carTarget);

// 放置小车在两个点中间
car.position.lerpVectors(carPosition, carTarget, 0.5);
});
}

当我运行它时,我发现每种汽车车,它们的高度都不是固定的,所以我要给每种车调整一下。

const loadedCars = root.getObjectByName('Cars');
const fixes = [
{ prefix: 'Car_08', rot: [Math.PI * .5, 0, Math.PI * .5], },
{ prefix: 'CAR_03', rot: [0, Math.PI, 0], },
{ prefix: 'Car_04', rot: [0, Math.PI, 0], },
{ prefix: 'Car_08', y: 0, rot: [Math.PI * .5, 0, Math.PI * .5], },
{ prefix: 'CAR_03', y: 33, rot: [0, Math.PI, 0], },
{ prefix: 'Car_04', y: 40, rot: [0, Math.PI, 0], },
];

root.updateMatrixWorld();
for (const car of loadedCars.children.slice()) {
const fix = fixes.find(fix => car.name.startsWith(fix.prefix));
const obj = new THREE.Object3D();
car.getWorldPosition(obj.position);
car.position.set(0, 0, 0);
car.position.set(0, fix.y, 0);
car.rotation.set(...fix.rot);
obj.add(car);
scene.add(obj);
cars.push(obj);
}

结果是

click here to open in a separate window

几分钟运行的还不错
最后我想做的是开启阴影
我获取了所有 DirectionalLight的GUI代码, the article on shadows中的阴影实例 。并粘贴到最新代码
加载完之后,我们需要开启所有物体的阴影

{
const gltfLoader = new GLTFLoader();
gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {
const root = gltf.scene;
scene.add(root);

root.traverse((obj) => {

if (obj.castShadow !== undefined) {
obj.castShadow = true;
obj.receiveShadow = true;
}
});

我花了将近4小时,尝试搞清楚为啥shadow helpers不工作。因为我忘记了用以下代码开启阴影

renderer.shadowMap.enabled = true;

😭
I then adjusted the values until our DirectionLight的阴影 相机的视锥能覆盖所有场景。 我最后完成的设置

{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.castShadow = true;
light.position.set(-250, 800, -850);
light.target.position.set(-550, 40, -450);

light.shadow.bias = -0.004;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;

scene.add(light);
scene.add(light.target);
const cam = light.shadow.camera;
cam.near = 1;
cam.far = 2000;
cam.left = -1500;
cam.right = 1500;
cam.top = 1500;
cam.bottom = -1500;
...

我设置背景为蓝色

const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
scene.background = new THREE.Color('#DEFEFF');

阴影

click here to open in a separate window

我希望详细讲完这个工程是有用的,并且能作为 在使用场景图(scenegraph)加载文件时 解决一些问题(issues)很好的案例。
有一件趣事,当对比.blend文件时与.gltf文件时,.blend 文件有多个灯光(lights),但加载到场景中后它们不是灯光(lights) 一个.GLTF文件只是JSON文件,因此你能查看它内部。 它由几个数组构成(scenes,nodes, meshes,accessors,materials...),数组中每一项都通过索引来引用其他项。 虽然工作中有扩展,但它们指出了几乎所有 3d 格式的问题。 它们无法涵盖任何情况.
总是需要更多的数据。 例如,我们手动导出的车辆绕着跑的路径。 理想的情况是,这些信息可以成为GLTF文件。但是这么做。我们需要自己写导出器,以及一些如何标记节点以了解我们希望它们如何导出或使用命名方案或类似的东西来从我们使用的任何工具获取数据 用于将数据创建到我们的应用程序中。
所有这些练习都留给了读者了。

相关文章:

three.js中怎么加载 .gltf/glb格式 文件

3D编辑器格式 用于特定应用程序(主要是3D编辑器):. .blend (Blender), .max (3d Studio Max), .mb and .ma (Maya), etc... 交换格式 有.OBJ, .DAE (Collada), .FBX.等格式。它们被设计出来用于3D编辑器之间交换信息的。因此,它们通常比所需的大得多(内含3D编辑器内所需要的…...

ollama如何安装使用

Ollama 是目前最流行的本地大语言模型(LLM)运行和管理的工具。它让下载、运行和体验各种开源模型(如 Llama、Mistral、Gemma 等)变得极其简单。 下面是从安装到使用的完整指南。一、安装 Ollama Ollama 支持 macOS、Linux 和 Windows(预览版)。 1. macOS方式一(推荐):…...

【SPIE独立出版|连续多年EI稳定检索】第七届地球科学与遥感测绘国际学术会议(GRSM 2025)

为交流近年来国内外在地球、地理科学和遥感测绘领域的理论、技术和应用的最新进展,展示最新成果,第七届地球科学与遥感测绘国际学术会议将于2025年10月17-19日在中国-乌鲁木齐召开,GRSM 2025旨在为从事地球科学和遥感测绘领域的专家学者、工程技术人员、技术研发人员提供一个…...

手把手教你实现C++高性能内存池,相比 malloc 性能提升7倍!

大家好,我是小康。 写在前面 你知道吗?在高并发场景下,频繁的malloc和free操作就像是程序的"阿喀琉斯之踵",轻则拖慢系统响应,重则直接把服务器拖垮。 最近我从0到1实现了一个高性能内存池,经过严格的压测验证,在8B到2048B的分配释放场景下,性能相比传统的ma…...

LDPC 码 BP 算法性能研究

1. 要点来源:NASA 技术报告 + IEEE 802.16e 标准矩阵 算法:经典 Belief-Propagation(BP)迭代解码 + 最小和(MS)变体 性能:BER vs Eb/N0、迭代次数、码长/码率影响、BP vs MS 对比 输出:BER 曲线图、迭代收敛图、误码位置分布2. 结构 LDPC_BP_Study/ ├── main_…...

内外网文件传输方式有哪些:从传统方案到专业系统的全面解析!

在数字化时代,企业数据流通需求日益增长,许多企业为保障核心数据安全实施网络隔离,由此催生了内外网文件传输的高频场景。内外网文件传输指在企业内部网络与外部网络(或不同安全域隔离网络)之间,实现文件数据流通的行为,其核心诉求是在突破网络连接限制的同时,兼顾安全…...

实用指南:DeerFlow 实践:华为IPD流程的评审智能体设计

实用指南:DeerFlow 实践:华为IPD流程的评审智能体设计pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", mon…...

py之补环境代理脚本

function getEnvs(proxyObjs) {for (let i = 0; i < proxyObjs.length; i++) {const handler = `{get: function(target, property, receiver) {console.log("方法:", "get ", "对象:", "${proxyObjs[i]}", " 属性:", …...

Python psutil模块

一、简介psutil(Process and System Utilities)是Python中最强大的系统监控和进程管理库之一。它提供了跨平台的系统信息获取接口,能够轻松获取系统的CPU、内存、磁盘、网络等硬件信息,以及进程管理、系统监控等功能。无论是开发运维工具、系统监控程序,还是性能分析应用,…...

跨网文件传输工具选择要点是什么?

内容概要 在选择跨网文件传输工具时,企业面临着诸多考量。本文旨在全面剖析“跨网文件传输工具如何选择”的关键要素,特别是聚焦于像Ftrans Ferry跨网文件安全交换系统这样的专业解决方案。以下将从安全性、功能特性、易用性及扩展性四大维度出发,通过具体分析和实例,为您提…...

下半年业绩冲刺,12个效率工具帮你提速!

三季度收尾在即,距离 2025 年结束只剩 3 个月,你的业绩目标还差多少? 如果进度滞后,四季度的冲刺就得靠 “巧劲”—— 别只拼时间,多借工具的力。就像我一位朋友,刚工作时同事还在手写记录、靠电话找信息,他早早用电脑打字、上网查资料,效率直接领先一截。古人说 “君子…...

AUTOSAR的MPU内存保护

AUTOSAR的MPU内存保护Q:How can I configure MPU(Memory Protection Unit) for an OS_Task in Vector AUTOSAR Conguration Tool? A:For a configured task, you configure OsTaskMemoryProtectionIdentifier to specify a memory protection identifier for the task. you …...

AI赋能CRM:纷享销客智能图像提升终端运营效率

在消费品行业竞争日益白热化的当下,终端已成为品牌竞争的核心战场。门店陈列是否规范、数据是否真实、执行是否合规,让诸多企业陷入“看不见、管不清、控不住”的终端管理困境。 面对这一核心痛点,纷享销客推出全新升级的纷享 AI 图像智能,以 AI 技术重新定义终端管理模式。…...

【linux命令】网卡命令 ①

网卡命令 ifup / ifdown功能:启用或关闭网络接口。 依赖配置文件:在 RHEL/CentOS 系统,读取 /etc/sysconfig/network-scripts/ifcfg-*。用法: ifup eth0 # 启用 eth0 ifdown eth0 # 关闭 eth0限制:如果没有写配置文件,会提示 not configured,所以更偏向于系统初…...

一款基于 .NET 开源美观、功能丰富的串口调试工具

前言 今天大姚给大家分享一款基于 .NET 开源(GPL-3.0 license)、美观、流畅、高效、功能丰富的串口调试工具:SuperCom。 工具介绍 SuperCom 是一款基于 .NET 开源(GPL-3.0 license)、美观、流畅、高效、功能丰富的串口调试工具,主要用于 Window 串口日志的采集、存储、可…...

麒麟系统中docker常用命令

检查 docker 服务是否已启动 sudo systemctl status dockersudo systemctl start docker # 启动 sudo systemctl enable docker # 设置开机自启 sudo systemctl restart docker #重启docker查看 docker 镜像和容器docker images # 列出本地所有镜像 docker…...

2015年7月微软安全更新深度解析:SQL Server、IE及系统漏洞修复

微软2015年7月发布14项安全更新,含4项紧急补丁,涉及SQL Server远程代码执行、IE浏览器漏洞、Hyper-V虚拟化风险等关键技术领域,同时提供Windows Server 2003最终安全更新和恶意软件清除工具增强。2015年7月安全更新概要 2015年7月15日(日本时间),微软发布14项新安全公告(…...

国产传输软件解决方案厂商优选指南

内容概要 在当今数字化时代,选择合适的国产传输软件解决方案厂商对于企业的信息安全与业务效率至关重要。众多厂商中,国产传输软件解决方案厂商以其专业实力脱颖而出,其中Ftrans SFT文件安全传输系统‌备受瞩目。本指南旨在通过深入分析厂商的全面适配信创生态能力、解决方案…...

安卓方程式计算器HiPER Calc PRO v11.2.7 高级版

HiPER Calc Pro(原应用名称HiPER Scientific Calculator高级版),一款收费非常好用且无广告功能强大的方程式图形计算器,分数计算器,代数计算器,矩阵计算器,具备完整图形、算术方程式、表格、常数、函数强大功能。它多包含100位有效数字和9位小数,支持检测重复的小数,并…...

.NET 10 是微软 AI 战略的技术承重墙

微软于2025年1月成立的新部门 CoreAI - Platform and Tools(简称CoreAI),是其AI战略升级的核心举措,旨在整合内部资源、加速AI基础设施与工具开发,推动“以模型为中心”的应用变革。CoreAI是微软AI战略的“中枢神经”,通过整合开发工具、云平台与研究资源,构建覆盖全场景…...

在Oracle中,如何彻底停止expdp进程?

许多同事在使用expdp命令时,不小心按了CTRL+C组合键,然后又输入exit命令(或者网络中断等异常现象),导致expdp进程不存在,但Oracle数据库的会话仍存在,所以dmp文件也一直在增长。在这种情况下的处理办法如下所示: 1、检查expdp进程是否还在 ps -ef | grep expdp 若存在,…...

[SDR] 两种方法可视化打飞机:ADS-B 信号接收与航班追踪

目录效果展示一、前言二、gr-air-modes2.1 环境构建2.2 经典案例效果展示一、前言介绍ADS-B协议和1090MHz频段。 使用USRP和dump1090等工具接收和解码航班信息。 在地图上实时显示航班位置。二、gr-air-modes gr-air-modes 是一个开源的软件定义无线电(SDR)接收器,用于解析飞…...

P7072 [CSP-J2020] 直播获奖

P7072 [CSP-J2020] 直播获奖#include <bits/stdc++.h> using namespace std;const int maxn = 1e5 + 10; int n,w,cnt[610] = { 0 };int main(){ios::sync_with_stdio(0);cin.tie(0);cin >> n >> w;int ans = 1;for(int i = 1; i <= n; i++){int t;cin &g…...

服务器文件同步工具大盘点

内容概要 在数据驱动的时代,服务器文件同步成为企业日常运营不可或缺的一环。面对繁多的文件同步需求,“服务器文件同步工具有哪些”成为众多IT管理者关心的焦点。本文将全面盘点当前主流的服务器文件同步工具,特别介绍如Ftrans FTC文件传输管控系统等高效解决方案。它们凭借…...

基于Python+Vue开发的酒店客房预订管理系统源码+运行步骤

项目简介该项目是基于Python+Vue开发的酒店客房预订管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Python的酒店客房预订管理系统项目,大学生可以在实践…...

AI破局俱乐部,通过AI搞副业,确实解决了很多人中年危机问题

前段时间“李一舟”事件炒得沸沸扬扬,有的人悲愤不已,有的人拍手叫好。割韭菜的情景,就如你满怀期待地报班学一门课程,结果发现所谓的“秘籍”不过是些网上随处可见的常识。又如你兴冲冲地去吃大餐,结果端上来的却是昨天的剩饭。这种“割韭菜”的套路,简直就是在考验我们…...

深入剖析布谷im即时通讯app系统软件源码之技术

随着互联网的发展,企业和个人对即时通讯(IM)应用的需求日益增长。山东布谷鸟网络科技有限公司深入了解市场与用户需求,专注于即时通讯app系统的开发,提供包括源码交付的完整解决方案,助力客户快速搭建安全、稳定且高效的IM平台。 山东布谷科技将从软件开发的多个维度,详…...

解锁RAG高阶密码:自适应、多模态、个性化技术深度剖析

别让你的AI系统还停留在只会查字典的阶段!本文用轻松幽默的方式揭秘高级RAG技术如何让AI变得更聪明:自适应检索像读心术一样精准,多模态RAG让AI能看图识字,个性化RAG则让AI记住你的每一个小习惯。想打造真正智能的AI应用?这三项技能缺一不可!当你走进一家智能咖啡厅,第一…...

低延迟垃圾收集器:挑战“不可能三角”

----------- 先赞后看 👍 效果翻倍 🔥 ---------------- 在开始之前,必须再次强调 “不可能三角”:内存占用、吞吐量、延迟,三者难以同时完美。 传统的垃圾收集器(如 Serial, Parallel, CMS, G1)在堆内存变大时,停顿时间(Latency)也会显著变长,因为它们总有一些阶…...

Java Heap Dump详解

1、什么是 Heap dump? Java堆转储(Heap Dump)是Java虚拟机(JVM)在某一时刻内存使用情况的快照,它包含了该时刻所有对象的实例信息、类信息、字段值和引用关系等数据。堆转储文件通常以 .hprof或 .heap为扩展名,是诊断内存问题的关键工具。 当应用程序出现内存泄漏、内存…...

软件逆向入门理论

软件逆向入门理论目录大脉络软件的构成软件逆向是一种技术手段,它能达成的目的:分析该软件的漏洞; 分析这个软件,想作为跳板获取其他软件、系统的更高权限; 分析该软件的安全性、可靠性等; 分析该软件的构成;下面粗浅的进行软件逆向入门,将选择经典的x86平台下的软件来…...

鸿蒙应用开发从入门到实战(七):ArkTS组件声明语法

《鸿蒙应用开发从入门到项目实战》系列文章持续更新中,陆续更新AI+编程、企业级项目实战等原创内容、欢迎关注!​本文从界面制作从组件声明开始,通过一个相对简单的案例来系统的学习 ArkTS 声明组件的语法。大家好,我是潘Sir,持续分享IT技术,帮你少走弯路。《鸿蒙应用开发…...

P1115 最大子段和

P1115 最大子段和 妙#include <bits/stdc++.h> using namespace std; #define int long long const int maxn = 2e5 + 10;int n,a[maxn],dp[maxn]; int ans = -100010;signed main(){// freopen("in","r",stdin);ios::sync_with_stdio(0);cin >&g…...

麒麟系统关闭防火墙

有时远端连接数据库时出现 连接超时 ,则有可能是 麒麟系统的防火墙 开启了,最简单的方法是关闭防火墙#查看防火墙状态 sudo systemctl status firewalld #检查 firewalld 是否运行 #或 sudo systemctl status ufw #检查 ufw 是否运行 (部分系统适用)#临时停止防火…...

P14003 [eJOI 2025] Reactions 解题报告

题目意思 随便找的题目,树状数组和离散化,很板子的东西不会细说 给定两个数组 \(D_i,T_i\),求 \(\max\limits_{i=1}^{n}{(\sum\limits_{j=i}^n[\sum\limits_{k=i}^jD_k \ge T_j])}\)。 思路 首先如果你将上面那个式子分解或者稍稍理解一下就能想到将区间加和,区间问题转化为…...

计算机科学入门

计算机科学入门 总结:知识点体系图 graph TDA[C++基础知识点] --> B[计算机发展史]A --> C[操作系统]A --> D[硬件组成]A --> E[编程语言与开发]A --> F[基本操作]B --> B1[重要人物:图灵/冯诺依曼]B --> B2[历史里程碑:ENIAC/103机]B --> B3[奖项:…...

Windows Server 2019开启远程桌面无法远程处理

故障现象 Windows Server 2019开启了远程桌面但是无法远程 Telnet对应端口3389也不通 故障原因 远程服务Down了 修复 到服务管理重启远程服务Remote Desktop Service如果提示病毒则关闭本机自带杀毒...

英语_阅读_This makes me crazy_待读

My parents know little about high-tech products. 我的父母对高科技产品几乎一无所知。 Computers, mobile phones, iPads ... They cant use any of them! 电脑、手机、平板……他们一个都不会用! Every time Im home for a holiday, my parents ask me how to take a phot…...

一位华裔数学家40年目睹之怪现状:美国学生的数学为什么那么差?

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087最近在《知识分子》上读到一篇特写《数学战争警示录:我们需要什么样的数学教育?》,颇有感触。在这俄乌战争已持续三年有余、…...

这些年轻科学家不再满足于“追赶美国”

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087 添加图片注释,不超过 140 字(可选)在很长一段时间里,硅谷是全球AI人才的终点站。而中国曾是最大的人才输出地,把一代又一…...

英语_阅读_

My parents know little about high-tech products. 我的父母对高科技产品几乎一无所知。 Computers, mobile phones, iPads ... They cant use any of them! 电脑、手机、平板……他们一个都不会用! Every time Im home for a holiday, my parents ask me how to take a phot…...

聊聊理想的影像团队

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087这实际上是一个招聘贴,期望能捞一些理念相同的同道中人,一起做一些事情,为视觉中枢在 AI 浪潮里留下点什么,为 AI 做出点什…...

黑芝麻智能上半年亏损超7亿 CEO单记章去年薪酬高达1.66亿

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087 添加图片注释,不超过 140 字(可选) 添加图片注释,不超过 140 字(可选)国内“智驾芯片第一股”黑芝麻智能国际控股有限…...

英语_阅读_BMI_待读

56. What is Chinas recent success in BMI? 中国在脑机接口方面最近的成功是什么? A paralyzed patient was able to drink water by controlling a glove with his brain. 一位瘫痪患者能够通过用大脑控制手套来喝水。57. What is the external device in the recent exper…...

Flutter数据可视化:fl_chart图表库的高级应用

Flutter数据可视化:fl_chart图表库的高级应用本文基于BeeCount(蜜蜂记账)项目的实际开发经验,深入探讨如何使用fl_chart构建美观、交互式的财务数据可视化图表。项目背景 BeeCount(蜜蜂记账)是一款开源、简洁、无广告的个人记账应用。所有财务数据完全由用户掌控,支持本地存…...

教材大纲-Python

版本一:教材大纲-Python 1 编程世界初探 1.1 编程世界初探 1.2 初识Python语言 1.3 迈出Python编程的第一步 2 走近算法与Python基础 2.1 走近算法:流程图 2.2 触摸生活中的标志:turtle 2.3 初探Python基础知识:常见数据标识与语句 2.4 体会程序编写规范:命名与注释 3 Pyt…...

2025 年 PHP 常见面试题整理以及对应答案和代码示例

2025 年 PHP 常见面试题整理以及对应答案和代码示例 PHP 面试通常会考察基础知识(数组、OOP、错误处理)和现代特性(类型、属性、枚举)。关键是要展示你能写出简洁、可预测的代码,同时了解 PHP 8+ 的新变化。 我整理了以下一些常见 PHP 可能面试的。每个问题都有简洁的答案…...

0130_中介者模式(Mediator)

中介者模式(Mediator) 意图 用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 UML 图优点降低耦合度:将对象间的一对多关联转变为一对一的关联,减少对象间的依赖 集中控制:将交互逻辑集…...

零门槛入局 AI 创业!瓦特 AI 创作者平台,让普通人轻松抓住风口

刷短视频时,总被 AI 生成的炫酷图片、洗脑音乐吸引;和朋友聊天,大家都在热议 “AI 变现”—— 你是不是也想搭上这波 AI 浪潮,却总被 “要懂技术”“需复杂开发” 拦住脚步?别担心,瓦特 AI 自媒体创作者平台早已为普通人铺好赛道:无需技术基础、一键部署专属平台,轻松拥…...

基环树

一.首先定义看定义 树是N个点N-1条边的联通图 基环树是N个点N条边的连通图 不保证联通就都是森林 所以基环树就是在树上加了一条边,使得树上有了一个环 基环树的常见处理方法把环上的一条边单独处理, 这样其余部分依然是一棵树 把环单独处理, (缩成一个点)这样其余部分依然…...