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

使用Three.js搭建自己的3Dweb模型(从0到1无废话版本)

教学视频参考:B站——Three.js教学

教学链接:Three.js中文网  老陈打码 | 麒跃科技

一.什么是Three.js?

Three.js​ 是一个基于 JavaScript 的 ​3D 图形库,用于在网页浏览器中创建和渲染交互式 3D 内容。它基于 WebGL(一种浏览器原生支持的 3D 图形 API),但通过更简单的抽象层让开发者无需直接编写复杂的 WebGL 代码即可构建 3D 场景。

下面是官网链接:基础 - three.js manual、three.js docs

二.入门 —— Vue3编写一个可旋转的正方体页面

在App.vue内编写代码:

首先初始化基础环境:

// 1.1 创建场景(容器)
const scene = new THREE.Scene();// 1.2 创建透视相机
const camera = new THREE.PerspectiveCamera(75, // 视野角度window.innerWidth / window.innerHeight, // 宽高比0.1, // 近裁剪面1000 // 远裁剪面
);// 1.3 创建WebGL渲染器(启用抗锯齿)
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染尺寸
document.body.appendChild(renderer.domElement); // 将画布添加到页面

当调用new THREE.WebGLRenderer()时,Three.js会自动创建一个<canvas>元素,以至于我们通过renderer.domElement可以获取这个canvas,并通过

document.body.appendChild(renderer.domElement)直接将canvas插入body。

(这就是不写<canvas>也可以渲染的原因)

随后创建3D正方体:

参数类型作用
geometryTHREE.BufferGeometry定义物体的形状(如立方体、球体等)
materialTHREE.Material定义物体的外观(颜色、纹理、反光等)
// 2.1 创建立方体几何体
const geometry = new THREE.BoxGeometry(1, 1, 1); // 1x1x1的立方体// 2.2 创建绿色基础材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });// 2.3 组合几何体和材质为网格对象
const cube = new THREE.Mesh(geometry, material);// 2.4 将立方体添加到场景
scene.add(cube);

 之后设置相机位置:

这里是直接设置成在z轴并对准原点

camera.position.z = 5; // 相机沿z轴后退5个单位
camera.lookAt(0, 0, 0); // 相机对准场景中心

最后使用递归animate()方法不断调用来让正方体展示并旋转:

function animate() {requestAnimationFrame(animate); // 循环调用自身cube.rotation.x += 0.01; // x轴旋转cube.rotation.y += 0.01; // y轴旋转renderer.render(scene, camera); // 渲染场景
}
animate(); // 启动动画

下面是完整代码: 

<script setup>
import * as THREE from 'three'// 创建场景
const scene = new THREE.Scene();// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // 视野角度, 宽高比, 最近可见距离, 最远可见距离// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器大小
document.body.appendChild(renderer.domElement); // 将渲染器添加到页面中// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1); // 创建一个立方体几何体
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 创建一个绿色的材质
const cube = new THREE.Mesh(geometry, material); // 创建一个网格对象
scene.add(cube); // 将网格对象添加到场景中
// 设置相机位置
camera.position.z = 5; // 设置相机位置
camera.lookAt(0, 0, 0); // 设置相机朝向原点
// 渲染循环
function animate() {requestAnimationFrame(animate); // 请求下一帧动画cube.rotation.x += 0.01; // 旋转立方体cube.rotation.y += 0.01; // 旋转立方体// 渲染renderer.render(scene, camera); // 渲染场景和相机
}
animate(); // 开始动画循环</script><template><div></div>
</template><style scoped>
* {margin: 0;padding: 0;
}/* 3D效果都是画在canvas画布上 */
canvas{display: block;position: fixed;left: 0;top: 0;width: 100vw;height: 100vh;
}
</style>

 三. 基础操作

1.坐标辅助器与轨道辅助器

坐标辅助器(AxesHelper)是可视化 ​3D 坐标系​(X/Y/Z 轴),能够帮助开发者快速理解场景的空间方向。

  • X轴(红色)​​:水平向右
  • Y轴(绿色)​​:垂直向上
  • Z轴(蓝色)​​:垂直于屏幕(正向朝外)
import * as THREE from 'three';// 创建场景
const scene = new THREE.Scene();// 添加坐标辅助器(参数:坐标轴长度)
const axesHelper = new THREE.AxesHelper(5); // 5个单位长度
scene.add(axesHelper);

由于我们的相机正对着z轴拍摄,所以z轴只是一个点。在上图可以清晰的看见y轴x轴。

而我们想要用鼠标来改变相机的位置就需要使用轨道控制器:

轨道控制器:

  • 允许用户 ​用鼠标交互控制相机,实现:
    • 旋转​(左键拖动)
    • 缩放​(滚轮)
    • 平移​(右键拖动)
  • 适用于 ​调试 3D 场景​ 或 ​交互式展示
<script setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'// 创建场景
const scene = new THREE.Scene();// 初始化相机和渲染器
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);// 设置相机位置
camera.position.z = 5;
camera.lookAt(0, 0, 0);// 添加坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;// 动画循环
function animate() {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);
}
animate();// 处理窗口大小变化
window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
});
</script><template><!-- 空模板即可,Three.js会自动管理canvas --><div></div>
</template><style scoped>
* {margin: 0;padding: 0;
}canvas {display: block;position: fixed;left: 0;top: 0;width: 100vw;height: 100vh;
}
</style>

在这里缩放是我们的相机在不断的变换位置,以至于看到3D正方体不断的被我们拉动位置。

在这里可以设置是否带有阻尼,也就是是否带有惯性:

controls.enableDamping = true; // 启用阻尼(惯性效果)
controls.dampingFactor = 0.05; // 阻尼系数,越大停的越快
controls.autoRotate = true; // 设置旋转速度

 如果我们想要换一个对象监听,可以将轨道控制器 new OrbitControls(camera, renderer.domElement) 使用 new OrbitControls(camera, domElement.body) 来监听,同时要修改CSS:Controls – three.js docs

// 创建轨道控制器
const controls = new OrbitControls(camera, domElement.body);// 样式渲染(不写可能页面看不到)
body {width: 100vw;height: 100vh;
}

2.物体位移与父子元素

在 Three.js 中,理解物体位移和父子元素关系是构建复杂场景的基础。

Vector3 – three.js docs

每个 Three.js 物体(Object3D)都有 position 属性,它是一个 Vector3 对象,包含 x、y、z 三个分量:

const cube = new THREE.Mesh(geometry, material);  // 创建一个新的 3D 网格物体​(Mesh)// 设置位置
cube.position.set(1, 2, 3); // x=1, y=2, z=3// 或者单独设置
cube.position.x = 1;
cube.position.y = 2;
cube.position.z = 3;
// 也可以使用set方法
cube.position.set(1,2,3);

如何让其位移呢?

世界坐标 = 父级世界坐标 + 子级局部坐标

在讲解父子元素前需要了解 -> 

什么是局部坐标,什么是世界坐标呢?

相对坐标(局部坐标)世界坐标
定义相对于父级容器的坐标相对于场景原点的绝对坐标
表示object.position通过计算得到
影响受父级变换影响不受父级变换影响
用途物体在父容器内的布局场景中的绝对定位

 ​世界坐标 = 父级世界坐标 + 子级局部坐标

存在旋转/缩放时,必须用 getWorldPosition() 计算

 【1】相对坐标(局部坐标)

特点:

  • 存储在 object.position 中
  • 所有变换操作默认基于局部坐标系
  • 子对象继承父对象的变换

在 Three.js 中,const parent = new THREE.Group(); 用于创建一个空容器对象​(Group),它是组织和管理 3D 场景中多个物体的核心工具。 

  • 继承自 THREE.Object3D,但没有几何体(Geometry)和材质(Material)
  • 仅用于逻辑分组,自身不可见,不参与渲染
方法作用
.add(object1, object2...)添加子对象
.remove(object)移除子对象
.clear()清空所有子对象
.getObjectByName(name)按名称查找子对象
const parent = new THREE.Group();
parent.position.set(2, 0, 0);const child = new THREE.Mesh(geometry, material);
child.position.set(1, 0, 0); // 相对于父级的坐标parent.add(child);
// 此时child的局部坐标是(1,0,0),世界坐标是(3,0,0)
 【2】世界坐标

特点:

  • 物体在全局场景中的绝对位置
  • 需要计算得到(考虑所有父级变换)
  • 常用于碰撞检测、物理计算等
const worldPosition = new THREE.Vector3();
object.getWorldPosition(worldPosition);const worldRotation = new THREE.Quaternion();
object.getWorldQuaternion(worldRotation);const worldScale = new THREE.Vector3();
object.getWorldScale(worldScale);

 3.物体的缩放与旋转

在 Three.js 中,缩放(scale)和旋转(rotation)是物体变换(transform)的两个核心操作,它们与位移(position)共同构成了物体的完整空间变换。

Euler – three.js docs

Three.js 提供了多种旋转表示方式:(旋转顺序默认为 'XYZ')

  • rotation (欧拉角,默认)
  • quaternion (四元数)
// 分别绕各轴旋转
cube.rotation.x = Math.PI/4; // 绕X轴旋转45度
cube.rotation.y = Math.PI/2; // 绕Y轴旋转90度// 使用set方法
cube.rotation.set(Math.PI/4, 0, 0);

 旋转与父子关系:

const parent = new THREE.Group();
parent.rotation.y = Math.PI/2;const child = new THREE.Mesh(geometry, material);
child.position.set(1, 0, 0);
parent.add(child);// child会继承parent的旋转,其世界位置会变化

Three.js 的变换顺序是:​缩放 → 旋转 → 平移 

假如父组件被缩放,那么子组件也会跟着父组件被缩放的倍数进行缩放。

// 以下两个操作不等价
cube.scale.set(2, 1, 1);
cube.rotation.y = Math.PI/4;// 与
cube.rotation.y = Math.PI/4;
cube.scale.set(2, 1, 1);

4.画布自适应窗口:

在 Three.js 开发中,实现画布(Canvas)自适应窗口大小是创建响应式 3D 应用的基础。

// 监听窗口的变化
window.addEventListener('resize', () => {// 重置渲染器宽高比renderer.setSize(window.innerWidth, window.innerHeight);// 重置相机的宽高比camera.aspect = window.innerWidth / window.innerHeight;// 更新相机投影矩阵camera.updateProjectionMatrix();
});

现在注册一个按钮监听点击事件来让其全屏:

// 监听按钮点击事件
const button = document.createElement('button');
button.innerHTML = '点击全屏';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
button.style.zIndex = '1000';
button.style.backgroundColor = '#fff';
button.onclick = () => {// 全屏if (document.fullscreenElement) {document.exitFullscreen();} else {document.documentElement.requestFullscreen();}
};
document.body.appendChild(button);
// 监听全屏事件
document.addEventListener('fullscreenchange', () => {if (document.fullscreenElement) {button.innerHTML = '退出全屏';} else {button.innerHTML = '点击全屏';}
});

 左侧就是渲染的效果。

5.lilGUI

Lil-GUI(原名为 dat.GUI)是一个轻量级的JavaScript库,专门用于创建调试控制面板,特别适合Three.js等WebGL项目的参数调节。

下载依赖:

npm install lil-gui

导入lilGUI:

import GUI from 'three/examples/jsm/libs/lil-gui.module.min.js';

我们以实现全屏按钮为例:

// 监听按钮点击事件
const gui = new GUI();
// 定义事件
const event = { FullScreen: () => {document.documentElement.requestFullscreen();},ExitFullscreen: () => {document.exitFullscreen();},ChangeColor: () => {cube.material.color.set(Math.random() * 0xffffff);},
};
// 添加按钮
gui.add(event, 'FullScreen').name('全屏');
gui.add(event, 'ExitFullscreen').name('退出全屏');

左侧图片就是我们的渲染效果。

还可以使用lilGUI调节立方体的位置:

// 随机控制立方体位置
gui.add(cube.position, 'x', -5, 5).name('立方体X轴位置'); 
// 也可以是下面这样
gui.add(cube.position, 'x').min(-5).max(5).step(1).name('立方体X轴位置');

也可以使用folder创建下拉框:

const folder = gui.addFolder('立方体位置');
folder.add(cube.position, 'x', -5, 5).name('立方体X轴位置');
folder.add(cube.position, 'y', -5, 5).name('立方体Y轴位置');
folder.add(cube.position, 'z', -5, 5).name('立方体Z轴位置');

也可以绑定监听事件:

const folder = gui.addFolder('立方体位置');
folder.add(cube.position, 'x', -5, 5).onChange(() => {console.log('立方体X轴位置:', cube.position.x);}).name('立方体X轴位置');
folder.add(cube.position, 'y', -5, 5).name('立方体Y轴位置');
folder.add(cube.position, 'z', -5, 5).name('立方体Z轴位置');

也可以监听最后停下的事件:

folder.add(cube.position, 'y', -5, 5).onFinishChange(()=>{console.log('立方体Y轴位置:', cube.position.y);
}).name('立方体Y轴位置');

 也可以使用布尔值设置是否为线框模式:

const gui = new GUI();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
gui.add(material, 'wireframe').name('线框模式');

也可以选择颜色:

// 选择颜色
gui.addColor(material, 'color').name('颜色选择器').onChange((val) => {cube.material.color.set(val);console.log('立方体颜色:', material.color.getHexString());
});

四.几何体

几何体是 Three.js 中定义3D物体形状的基础组件。它们由顶点(vertices)、面(faces)、边(edges)等元素构成,决定了物体的基本形状和结构。 

BufferGeometry – three.js docs

1.几何体_顶点_索引 

由于一个矩形是由两个三角形构成,所以需要两组顶点数据(2*3=6)构造,下面的代码用来构造一个矩形:        

const geometry = new THREE.BufferGeometry();
// 创建一个简单的矩形. 在这里我们左上和右下顶点被复制了两次。
// 因为在两个三角面片里,这两个顶点都需要被用到。
// 创建顶点数据
const vertices = new Float32Array( [-1.0, -1.0,  1.0,1.0, -1.0,  1.0,1.0,  1.0,  1.0,1.0,  1.0,  1.0,-1.0,  1.0,  1.0,-1.0, -1.0,  1.0
] );// itemSize = 3 因为每个顶点都是一个三元组。
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
const mesh = new THREE.Mesh( geometry, material );

使用下面代码查看我们构造的矩形:

<template><div ref="container" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';const container = ref(null);onMounted(() => {// 1. 创建场景、相机和渲染器const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);const renderer = new THREE.WebGLRenderer({ antialias: true });// 2. 设置渲染器大小并添加到DOMrenderer.setSize(window.innerWidth, window.innerHeight);container.value.appendChild(renderer.domElement);// 3. 创建几何体和材质(线框模式)const geometry = new THREE.BufferGeometry();const vertices = new Float32Array([-1.0, -1.0,  1.0,1.0, -1.0,  1.0,1.0,  1.0,  1.0,1.0,  1.0,  1.0,-1.0,  1.0,  1.0,-1.0, -1.0,  1.0]);geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));// 使用MeshBasicMaterial并启用线框模式const material = new THREE.MeshBasicMaterial({ color: 0xff0000,wireframe: true  // 启用线框模式});const mesh = new THREE.Mesh(geometry, material);scene.add(mesh);// 4. 添加坐标轴辅助器(红色-X,绿色-Y,蓝色-Z)const axesHelper = new THREE.AxesHelper(5);scene.add(axesHelper);// 5. 添加网格辅助器(地面网格)const gridHelper = new THREE.GridHelper(10, 10);scene.add(gridHelper);// 6. 设置相机位置camera.position.set(3, 3, 5);camera.lookAt(0, 0, 0);// 7. 添加轨道控制器const controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true; // 启用阻尼效果controls.dampingFactor = 0.05;// 8. 动画循环const animate = () => {requestAnimationFrame(animate);controls.update(); // 更新控制器renderer.render(scene, camera);};animate();// 9. 窗口大小调整处理const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);};window.addEventListener('resize', onWindowResize);// 10. 组件卸载时清理onUnmounted(() => {window.removeEventListener('resize', onWindowResize);container.value?.removeChild(renderer.domElement);geometry.dispose();material.dispose();controls.dispose();});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;margin: 0;padding: 0;
}
</style>

也可以使用索引来索引顶点位置进行构建:

// 创建几何体 - 使用索引绘制
const geometry = new THREE.BufferGeometry();// 定义4个顶点(矩形只需要4个顶点,而不是之前的6个重复顶点)
const vertices = new Float32Array([-1.0, -1.0,  1.0,  // 顶点0 - 左下1.0, -1.0,  1.0,  // 顶点1 - 右下1.0,  1.0,  1.0,  // 顶点2 - 右上-1.0,  1.0,  1.0   // 顶点3 - 左上
]);// 定义索引(用2个三角形组成矩形)
const indices = new Uint16Array([0, 1, 2,  // 第一个三角形0, 2, 3   // 第二个三角形
]);// 设置几何体属性
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(new THREE.BufferAttribute(indices, 1)); // 1表示每个索引是1个数字

 2.几何体划分顶点组设置不同材质

下面代码展示了正方体每个面由不同的颜色组成:

<template><div ref="container" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';const container = ref(null);onMounted(() => {// 1. 创建场景、相机和渲染器const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);const renderer = new THREE.WebGLRenderer({ antialias: true });// 2. 设置渲染器renderer.setSize(window.innerWidth, window.innerHeight);container.value.appendChild(renderer.domElement);// 3. 创建多材质立方体const createMultiMaterialCube = () => {const geometry = new THREE.BoxGeometry(2, 2, 2);// 为每个面创建不同材质const materials = [new THREE.MeshBasicMaterial({ color: 0xff0000 }), // 右 - 红new THREE.MeshBasicMaterial({ color: 0x00ff00 }), // 左 - 绿new THREE.MeshBasicMaterial({ color: 0x0000ff }), // 上 - 蓝new THREE.MeshBasicMaterial({ color: 0xffff00 }), // 下 - 黄new THREE.MeshBasicMaterial({ color: 0xff00ff }), // 前 - 紫new THREE.MeshBasicMaterial({ color: 0x00ffff })  // 后 - 青];return new THREE.Mesh(geometry, materials);};const cube = createMultiMaterialCube();scene.add(cube);// 4. 添加辅助工具const axesHelper = new THREE.AxesHelper(5);scene.add(axesHelper);const gridHelper = new THREE.GridHelper(10, 10);scene.add(gridHelper);// 5. 设置相机camera.position.set(3, 3, 5);camera.lookAt(0, 0, 0);// 6. 添加控制器const controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;// 7. 动画循环const animate = () => {requestAnimationFrame(animate);cube.rotation.x += 0.01;cube.rotation.y += 0.01;controls.update();renderer.render(scene, camera);};animate();// 8. 响应式处理const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);};window.addEventListener('resize', onWindowResize);// 9. 清理onUnmounted(() => {window.removeEventListener('resize', onWindowResize);container.value?.removeChild(renderer.domElement);controls.dispose();});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;margin: 0;padding: 0;
}
</style>

3.threejs常见的几何体:

下面是网站链接:

常见的几何体

// 常见几何体
// BoxGeometry (立方体)
const geometry = new THREE.BoxGeometry(width, height, depth);
// SphereGeometry (球体)
const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
// CylinderGeometry (圆柱体)
const geometry = new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radialSegments);
// ConeGeometry (圆锥体)
const geometry = new THREE.ConeGeometry(radius, height, radialSegments);
// TorusGeometry (圆环)
const geometry = new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments);
// 平面几何体
// PlaneGeometry (平面)
const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments);
// CircleGeometry (圆形)
const geometry = new THREE.CircleGeometry(radius, segments);
// RingGeometry (环形)
const geometry = new THREE.RingGeometry(innerRadius, outerRadius, thetaSegments);

4.基础网络材质

Material – three.js docs

材质描述了对象objects的外观。它们的定义方式与渲染器无关, 因此,如果我们决定使用不同的渲染器,不必重写材质。

我们先准备一个平面的渲染代码:

<template><div ref="container" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'lil-gui';const container = ref(null);
let gui = null;
let controls = null;onMounted(() => {// 1. 初始化场景const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);container.value.appendChild(renderer.domElement);// 初始化 GUIgui = new GUI();// 创建平面const planeGeometry = new THREE.PlaneGeometry(1, 1);const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff,});const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);scene.add(planeMesh);// 设置相机位置camera.position.z = 3;camera.lookAt(0, 0, 0);// 添加轨道控制器controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;// 动画循环const animate = () => {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);};animate();// 窗口大小调整const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);};window.addEventListener('resize', onWindowResize);// 清理资源onUnmounted(() => {window.removeEventListener('resize', onWindowResize);if (gui) gui.destroy();if (controls) controls.dispose();planeGeometry.dispose();planeMaterial.dispose();renderer.dispose();container.value?.removeChild(renderer.domElement);});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;margin: 0;padding: 0;
} 
</style>

为了将指定照片作为纹理贴在上面,我们添加一个纹理加载器THREE.TextureLoader(),将指定路径的纹理贴在创建的平面上:

// 初始化 GUI
gui = new GUI();
// 创建纹理加载器
const textureLoader = new THREE.TextureLoader();
// 2. 创建平面
const planeGeometry = new THREE.PlaneGeometry(1, 1);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff,map: textureLoader.load('/src/assets/jinggai.jpg') // 纹理路径
});
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);

 然后设置允许透明度以及双面渲染:

// 初始化 GUI
gui = new GUI();
// 创建纹理加载器
const textureLoader = new THREE.TextureLoader();
// 2. 创建平面
const planeGeometry = new THREE.PlaneGeometry(1, 1);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff,map: textureLoader.load('/src/assets/jinggai.jpg'),side: THREE.DoubleSide, // 双面渲染transparent: true, // 透明
});
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);

然后插入hdr格式照片来作为我们的全景环境:

先导入RGBELoader:

import { RGBELoader } from 'three/examples/jsm/Addons.js';
<template><div ref="container" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
import { GUI } from 'lil-gui';const container = ref(null);
let gui = null;
let controls = null;onMounted(() => {// 1. 初始化场景const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);const renderer = new THREE.WebGLRenderer({ antialias: true,toneMapping: THREE.ACESFilmicToneMapping, // 启用色调映射toneMappingExposure: 1.0 // 设置曝光});renderer.setSize(window.innerWidth, window.innerHeight);renderer.outputColorSpace = THREE.SRGBColorSpace; // 设置色彩空间container.value.appendChild(renderer.domElement);// 2. 初始化 GUIgui = new GUI();const params = {envMapIntensity: 1.0,exposure: 1.0};// 3. 加载 HDR 环境贴图const rgbeLoader = new RGBELoader();rgbeLoader.load('/src/assets/environment.hdr', // 替换为你的HDR文件路径(texture) => {// 设置球形映射texture.mapping = THREE.EquirectangularReflectionMapping; // 设置场景环境贴图scene.environment = texture;scene.background = texture;// 可选:创建平面材质const planeGeometry = new THREE.PlaneGeometry(1, 1);const planeMaterial = new THREE.MeshStandardMaterial({color: 0xffffff,metalness: 0.5,roughness: 0.1,envMap: texture, // 使用环境贴图envMapIntensity: params.envMapIntensity});const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);scene.add(planeMesh);// GUI 控制gui.add(params, 'envMapIntensity', 0, 2).onChange((value) => {planeMaterial.envMapIntensity = value;});gui.add(params, 'exposure', 0, 2).onChange((value) => {renderer.toneMappingExposure = value;});},undefined, // 进度回调(error) => {console.error('加载HDR环境贴图失败:', error);});// 4. 添加光源(增强效果)const ambientLight = new THREE.AmbientLight(0x404040);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 1);directionalLight.position.set(1, 1, 1);scene.add(directionalLight);// 5. 设置相机camera.position.z = 3;camera.lookAt(0, 0, 0);// 6. 添加控制器controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;// 7. 动画循环const animate = () => {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);};animate();// 8. 窗口大小调整const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);};window.addEventListener('resize', onWindowResize);// 9. 清理资源onUnmounted(() => {window.removeEventListener('resize', onWindowResize);if (gui) gui.destroy();if (controls) controls.dispose();renderer.dispose();container.value?.removeChild(renderer.domElement);});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;margin: 0;padding: 0;
}
</style>

5.雾fog:

雾效(Fog)是 Three.js 中用于模拟大气效果的重要功能,它能创造深度感和距离感,使场景看起来更加真实。

const scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xcccccc, 10, 100); // 线性雾
scene.fog = new THREE.FogExp2(0xcccccc, 0.01); // 指数雾

下面以极其长的长方体为例展示雾的效果:

<template><div ref="container" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'lil-gui';const container = ref(null);
let gui = null;
let controls = null;onMounted(() => {// 1. 初始化场景const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,0.1,1000);const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);container.value.appendChild(renderer.domElement);// 2. 添加雾效scene.fog = new THREE.FogExp2(0xcccccc, 0.01); // 使用指数雾scene.background = new THREE.Color(0xcccccc); // 背景色与雾色一致// 3. 创建长形长方体const length = 50;  // 长度const width = 2;    // 宽度const height = 2;   // 高度const geometry = new THREE.BoxGeometry(width, height, length);const material = new THREE.MeshStandardMaterial({ color: 0x3498db,metalness: 0.3,roughness: 0.7});const longBox = new THREE.Mesh(geometry, material);scene.add(longBox);// 4. 添加地面参考平面const groundGeometry = new THREE.PlaneGeometry(100, 100);const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x2c3e50,side: THREE.DoubleSide});const ground = new THREE.Mesh(groundGeometry, groundMaterial);ground.rotation.x = -Math.PI / 2;ground.position.y = -height / 2;scene.add(ground);// 5. 添加光源const ambientLight = new THREE.AmbientLight(0x404040);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);directionalLight.position.set(10, 20, 10);scene.add(directionalLight);// 6. 设置相机位置camera.position.set(10, 10, 10);camera.lookAt(0, 0, 0);// 7. 添加控制器controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;// 8. 初始化GUIgui = new GUI();const fogParams = {color: '#cccccc',density: 0.01,type: 'exp2'};gui.addColor(fogParams, 'color').onChange(value => {scene.fog.color.set(value);scene.background.set(value);});gui.add(fogParams, 'density', 0, 0.1).onChange(value => {if (scene.fog instanceof THREE.FogExp2) {scene.fog.density = value;}});gui.add(fogParams, 'type', ['linear', 'exp2']).onChange(value => {if (value === 'linear') {scene.fog = new THREE.Fog(parseInt(fogParams.color.replace('#', '0x')), 5, 50);} else {scene.fog = new THREE.FogExp2(parseInt(fogParams.color.replace('#', '0x')), fogParams.density);}});// 9. 动画循环const animate = () => {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);};animate();// 10. 窗口大小调整const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);};window.addEventListener('resize', onWindowResize);// 11. 清理资源onUnmounted(() => {window.removeEventListener('resize', onWindowResize);gui?.destroy();controls?.dispose();renderer.dispose();container.value?.removeChild(renderer.domElement);});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;margin: 0;padding: 0;
}
</style>

五.GLTF加载器

GLTFLoader – three.js docs 

glTF(gl传输格式)是一种开放格式的规范 (open format specification), 用于更高效地传输、加载3D内容。该类文件以JSON(.gltf)格式或二进制(.glb)格式提供, 外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin)。一个glTF组件可传输一个或多个场景, 包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像机。

可以去下面链接获取3D模型:Log in to your Sketchfab account - Sketchfab

1.标准 GLTF 模型加载(未压缩)

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';const loader = new GLTFLoader();loader.load(// 参数1: 资源路径'/models/character.glb',// 参数2: 加载完成回调(gltf) => {// 3.1 模型预处理const model = gltf.scene;model.scale.set(0.8, 0.8, 0.8);// 3.2 材质适配model.traverse((node) => {if (node.isMesh) {node.material.fog = true; // 启用雾效影响node.castShadow = true;   // 启用阴影}});scene.add(model);},// 参数3: 加载进度回调(xhr) => {console.log(`加载进度: ${(xhr.loaded / xhr.total * 100).toFixed(1)}%`);},// 参数4: 错误处理(error) => {console.error('加载失败:', error);// 可在此处添加备用方案}
);

需同时有 .gltf(JSON 描述文件) + .bin(二进制数据) + 贴图 

2.压缩模型加载(.glb 格式)​

loader.load('/models/compressed/model.glb',(gltf) => {const model = gltf.scene;// 遍历模型设置阴影和材质model.traverse((node) => {if (node.isMesh) {node.castShadow = true;node.material.metalness = 0.1; // 修改材质参数示例}});scene.add(model);},undefined, // 不显示进度(error) => console.error(error)
);

 3.DRACO 压缩模型加载

 安装解码器:

npm install three/examples/jsm/libs/draco

将 draco 文件夹复制到 public/libs/ 下。

import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/libs/draco/'); // 设置解码器路径
loader.setDRACOLoader(dracoLoader);loader.load('/models/compressed/dragon.glb', // Draco压缩的模型(gltf) => {gltf.scene.scale.set(0.5, 0.5, 0.5);scene.add(gltf.scene);}
);

 下面是完整演示代码:

<template><div ref="container" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { GUI } from 'lil-gui';const container = ref(null);
let gui = null;
let controls = null;
let carModel = null; // 存储加载的汽车模型onMounted(() => {// ==================== 1. 初始化场景 ====================const scene = new THREE.Scene();// 创建透视相机 (视野角度, 宽高比, 近裁面, 远裁面)const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);// 创建WebGL渲染器(开启抗锯齿)const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);renderer.shadowMap.enabled = true; // 启用阴影container.value.appendChild(renderer.domElement);// ==================== 2. 设置雾效 ====================// 使用指数雾(颜色,密度)scene.fog = new THREE.FogExp2(0xcccccc, 0.02);// 设置背景色与雾色一致scene.background = new THREE.Color(0xcccccc);// ==================== 3. 添加光源 ====================// 环境光(柔和的基础照明)const ambientLight = new THREE.AmbientLight(0x404040, 0.5);scene.add(ambientLight);// 定向光(主光源,产生阴影)const directionalLight = new THREE.DirectionalLight(0xffffff, 1);directionalLight.position.set(5, 10, 7);directionalLight.castShadow = true;directionalLight.shadow.mapSize.width = 2048; // 阴影质量directionalLight.shadow.mapSize.height = 2048;scene.add(directionalLight);// ==================== 4. 添加地面 ====================const groundGeometry = new THREE.PlaneGeometry(20, 20);const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x3a3a3a,roughness: 0.8});const ground = new THREE.Mesh(groundGeometry, groundMaterial);ground.rotation.x = -Math.PI / 2; // 旋转使平面水平ground.receiveShadow = true; // 地面接收阴影scene.add(ground);// ==================== 5. 加载汽车模型 ====================const loader = new GLTFLoader();// 创建加载进度显示const progressBar = document.createElement('div');progressBar.style.cssText = `position: absolute;top: 10px;left: 10px;color: white;font-family: Arial;background: rgba(0,0,0,0.7);padding: 5px 10px;border-radius: 3px;`;container.value.appendChild(progressBar);// 开始加载模型loader.load(// 模型路径(注意:Vite会自动处理src/assets路径)'/models/car.glb', // 加载成功回调(gltf) => {carModel = gltf.scene;// 遍历模型所有部分carModel.traverse((child) => {if (child.isMesh) {// 确保所有网格都能投射阴影child.castShadow = true;// 确保材质受雾效影响child.material.fog = true;}});// 调整模型位置和大小carModel.position.y = 0.5; // 稍微抬高避免与地面穿插carModel.scale.set(0.8, 0.8, 0.8);// 计算模型中心点并居中const box = new THREE.Box3().setFromObject(carModel);const center = box.getCenter(new THREE.Vector3());carModel.position.sub(center);scene.add(carModel);progressBar.textContent = '汽车模型加载完成';setTimeout(() => progressBar.remove(), 2000);},// 加载进度回调(xhr) => {const percent = (xhr.loaded / xhr.total * 100).toFixed(2);progressBar.textContent = `加载进度: ${percent}%`;},// 加载失败回调(error) => {console.error('模型加载失败:', error);progressBar.textContent = '加载失败: ' + error.message;progressBar.style.color = 'red';});// ==================== 6. 设置相机 ====================camera.position.set(5, 2, 5); // 相机初始位置camera.lookAt(0, 0.5, 0); // 看向模型中心// ==================== 7. 添加控制器 ====================controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true; // 启用阻尼惯性controls.dampingFactor = 0.05; // 阻尼系数controls.minDistance = 3; // 最小缩放距离controls.maxDistance = 20; // 最大缩放距离// ==================== 8. GUI控制面板 ====================gui = new GUI();const fogParams = {color: '#cccccc',density: 0.02,type: 'exp2'};// 雾效控制const fogFolder = gui.addFolder('雾效设置');fogFolder.addColor(fogParams, 'color').onChange(value => {scene.fog.color.set(value);scene.background.set(value);});fogFolder.add(fogParams, 'density', 0.001, 0.1, 0.001).onChange(value => {if (scene.fog instanceof THREE.FogExp2) {scene.fog.density = value;}});fogFolder.add(fogParams, 'type', ['linear', 'exp2']).onChange(value => {if (value === 'linear') {scene.fog = new THREE.Fog(parseInt(fogParams.color.replace('#', '0x')), 5, 30);} else {scene.fog = new THREE.FogExp2(parseInt(fogParams.color.replace('#', '0x')), fogParams.density);}});fogFolder.open();// ==================== 9. 动画循环 ====================const animate = () => {requestAnimationFrame(animate);controls.update(); // 更新控制器renderer.render(scene, camera); // 渲染场景};animate();// ==================== 10. 窗口大小调整 ====================const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);};window.addEventListener('resize', onWindowResize);// ==================== 11. 组件卸载清理 ====================onUnmounted(() => {window.removeEventListener('resize', onWindowResize);gui?.destroy();controls?.dispose();renderer.dispose();container.value?.removeChild(renderer.domElement);});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;margin: 0;padding: 0;
}
</style>

 还有一种可以观看小车的外壳:

<template><div ref="container" class="three-container"><div v-if="loadingProgress < 100" class="loading-overlay"><div class="progress-bar"><div class="progress" :style="{ width: `${loadingProgress}%` }"></div></div><div class="progress-text">{{ loadingProgress.toFixed(0) }}%</div></div></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';const container = ref(null);
const loadingProgress = ref(0);
let controls = null;
let model = null;// 自适应调整模型大小和相机位置
function fitCameraToObject(camera, object, offset = 1.5) {const boundingBox = new THREE.Box3().expandByObject(object);const center = boundingBox.getCenter(new THREE.Vector3());const size = boundingBox.getSize(new THREE.Vector3());const maxDim = Math.max(size.x, size.y, size.z);const fov = camera.fov * (Math.PI / 180);let cameraZ = Math.abs((maxDim / 2) / Math.tan(fov / 2)) * offset;// 限制最小距离cameraZ = Math.max(cameraZ, maxDim * 0.5);camera.position.copy(center);camera.position.z += cameraZ;camera.lookAt(center);// 更新控制器目标if (controls) {controls.target.copy(center);controls.maxDistance = cameraZ * 3;controls.minDistance = maxDim * 0.5;controls.update();}
}onMounted(() => {// 1. 初始化场景const scene = new THREE.Scene();scene.background = new THREE.Color(0xf0f0f0);// 2. 设置相机(使用更大的远裁切面)const camera = new THREE.PerspectiveCamera(50, // 更小的FOV减少透视变形window.innerWidth / window.innerHeight,0.1,5000 // 增大远裁切面);// 3. 高性能渲染器配置const renderer = new THREE.WebGLRenderer({ antialias: true,powerPreference: "high-performance"});renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));renderer.setSize(window.innerWidth, window.innerHeight);renderer.shadowMap.enabled = true;renderer.shadowMap.type = THREE.PCFSoftShadowMap;container.value.appendChild(renderer.domElement);// 4. 添加雾效(范围更大)scene.fog = new THREE.FogExp2(0xf0f0f0, 0.002); // 更低的密度// 5. 增强光照const ambientLight = new THREE.AmbientLight(0x404040, 0.8);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);directionalLight.position.set(10, 20, 15);directionalLight.castShadow = true;directionalLight.shadow.mapSize.width = 2048;directionalLight.shadow.mapSize.height = 2048;directionalLight.shadow.camera.far = 500;scene.add(directionalLight);// 6. 添加地面网格辅助查看const gridHelper = new THREE.GridHelper(100, 50, 0x888888, 0xcccccc);scene.add(gridHelper);// 7. 加载模型(使用Vite的public目录)const loader = new GLTFLoader();loader.load('/models/car.glb', // 替换为你的模型路径(gltf) => {model = gltf.scene;// 7.1 启用所有子元素的阴影model.traverse((child) => {if (child.isMesh) {child.castShadow = true;child.receiveShadow = true;// 优化大模型材质if (child.material) {child.material.side = THREE.DoubleSide;child.material.shadowSide = THREE.BackSide;}}});scene.add(model);// 7.2 自适应调整相机和控制器fitCameraToObject(camera, model);// 7.3 添加辅助线框查看边界const bbox = new THREE.Box3().setFromObject(model);const bboxHelper = new THREE.Box3Helper(bbox, 0xffff00);scene.add(bboxHelper);loadingProgress.value = 100;},(xhr) => {loadingProgress.value = (xhr.loaded / xhr.total) * 100;},(error) => {console.error('加载失败:', error);loadingProgress.value = -1; // 显示错误状态});// 8. 控制器配置controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;controls.screenSpacePanning = true;controls.maxPolarAngle = Math.PI * 0.9; // 限制垂直旋转角度// 9. 响应式处理const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);// 如果模型已加载,重新调整相机if (model) fitCameraToObject(camera, model);};window.addEventListener('resize', onWindowResize);// 10. 动画循环const animate = () => {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);};animate();onUnmounted(() => {window.removeEventListener('resize', onWindowResize);controls?.dispose();renderer.dispose();});
});
</script><style scoped>
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: hidden;
}.loading-overlay {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;z-index: 100;
}.progress-bar {width: 300px;height: 20px;background: rgba(255,255,255,0.2);border-radius: 10px;overflow: hidden;margin-bottom: 10px;
}.progress {height: 100%;background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);transition: width 0.3s ease;
}.progress-text {color: white;font-family: Arial, sans-serif;text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
</style>

相关文章:

使用Three.js搭建自己的3Dweb模型(从0到1无废话版本)

教学视频参考&#xff1a;B站——Three.js教学 教学链接&#xff1a;Three.js中文网 老陈打码 | 麒跃科技 一.什么是Three.js&#xff1f; Three.js​ 是一个基于 JavaScript 的 ​3D 图形库&#xff0c;用于在网页浏览器中创建和渲染交互式 3D 内容。它基于 WebGL&#xff0…...

Redis远程链接应用案例

1.配置文件设置 打开配置文件redis.windows.conf&#xff0c;配置以下内容&#xff1a; 1.bind 0.0.0.0&#xff08;设置所有IP可访问&#xff09; 2.requirepass 1234.com&#xff08;密码设置&#xff09; 3.protected-mode no&#xff08;远程可访问&#xff09; 2.防火…...

STM32 定时器TIM

定时器基础知识 定时器就是用来定时的机器&#xff0c;是存在于STM32单片机中的一个外设。STM32总共有8个定时器&#xff0c;分别是2个高级定时器(TIM1、TIM8)&#xff0c;4个通用定时器(TIM2、TIM3、TIM4、TIM5)和2个基本定时器(TIM6、TIM7)&#xff0c;如下图所示: STM32F1…...

基于大模型的急性化脓性阑尾炎全程诊疗预测与方案研究

目录 一、引言 1.1 研究背景与意义 1.2 国内外研究现状 1.3 研究目标与方法 二、大模型技术原理与应用基础 2.1 大模型概述 2.2 相关技术原理 2.3 数据收集与预处理 三、术前风险预测与准备 3.1 病情评估指标分析 3.2 大模型预测方法与结果 3.3 术前准备方案 四、…...

第一个 servlet请求

文章目录 前端后端前后端 产生 联系 前端 后端 package com.yanyu;import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse;import java.io.I…...

XLSX.utils.sheet_to_json设置了blankrows:true,但无法获取到开头的空白行

在用sheetJs的XLSX库做导入&#xff0c;遇到一个bug。如果开头行是空白行的话&#xff0c;调用sheet_to_json转数组获得的数据也是没有包含空白行的。这样会导致在设置对应的起始行时&#xff0c;解析数据不生效。 目前是直接跳过了开头的两行空白行 正确应该获得一下数据 问…...

python一款简单的数据库同步dts小实现

一、实现说明 在数据开发与运维场景中&#xff0c;数据库同步是一项基础且高频的需求。无论是开发环境与生产环境的数据镜像&#xff0c;还是多数据库之间的数据分发&#xff0c;都需要可靠的同步工具。本文将基于 Python 和pymysql库&#xff0c;实现一个轻量级数据库同步工具…...

误触网络重置,笔记本电脑wifi连接不上解决方法(Win10,Win11通用)

笔记本电脑连接不上网&#xff0c;有人说网络重置按钮可以解决&#xff0c;结果把wifi图标都给搞没了&#xff0c;只剩飞行模式&#xff0c;解决方法&#xff08;错误码39&#xff09;&#xff0c;罪魁祸首就是这个网络重置&#xff0c;一下连网络都检测不到了 那么没有网络怎…...

markdown-it-katex 安装和配置指南

markdown-it-katex 是一个用于 Markdown-it 的插件&#xff0c;旨在通过 KaTeX 库在 Markdown 文档中添加数学公式支持。KaTeX 是一个快速渲染数学公式的库&#xff0c;相比于 MathJax&#xff0c;它在性能上有显著优势。 步骤 1: 安装 Markdown-it 首先&#xff0c;你需要安装…...

开源财务软件:企业财务数字化转型的有力工具

在当今数字化时代&#xff0c;企业财务数字化转型已成为必然趋势。随着业务的不断拓展和复杂化&#xff0c;企业对财务软件的需求也在日益增长。然而&#xff0c;传统商业财务软件往往伴随着高昂的授权费用和有限的定制化能力&#xff0c;这让许多企业&#xff0c;尤其是中小企…...

大模型——Suna集成浏览器操作与数据分析的智能代理

大模型——Suna集成浏览器操作与数据分析的智能代理 Suna 是 Kortix AI 开发的一个开源通用 AI 代理,托管在 GitHub 上,基于 Apache 2.0 许可证,允许用户免费下载、修改和自托管。它通过自然语言对话帮助用户完成复杂任务,如网页浏览、文件管理、数据抓取和网站部署。Suna…...

QT中的事件及其属性

Qt中的事件是对操作系统提供的事件机制进行封装&#xff0c;Qt中的信号槽就是对事件机制的进一步封装 但是特殊情况下&#xff0c;如对于没有提供信号的用户操作&#xff0c;就需要通过重写事件处理的形式&#xff0c;来手动处理事件的响应逻辑 常见的Qt事件&#xff1a; 常见事…...

flutter 选择图片 用九宫格显示图片,右上角X删除选择图片,点击查看图片放大缩小,在多张图片可以左右滑动查看图片

flutter 选择图片 用九宫格显示图片&#xff0c;右上角X删除选择图片&#xff0c;点击查看图片放大缩小&#xff0c;在多张图片可以左右滑动查看图片 ************ 暂无压缩图片功能 ********* 显示图片 — import dart:io;import package:flutter/material.dart; import pa…...

机器学习day2-seaborn绘图练习

1.使用tips数据集&#xff0c;创建一个展示不同时间段(午餐/晚餐)账单总额分布的箱线图 import seaborn as sns import matplotlib.pyplot as plt import pandas as pd import numpy as np# 设置主题 sns.set_theme(style"darkgrid")# 设置中文 plt.rcParams[font.s…...

如何优雅地解决AI生成内容粘贴到Word排版混乱的问题?

随着AI工具的广泛应用&#xff0c;越来越多人开始使用AI辅助撰写论文、报告或博客。然而&#xff0c;当我们直接将AI生成的文本复制到Word文档中时&#xff0c;常常会遇到排版混乱、格式异常的问题。这是因为大部分AI输出时默认使用了Markdown格式&#xff0c;而Word对Markdown…...

设计一个食品种类表

需求&#xff1a;设计一个食品种类表&#xff0c;注意食品种类有多层&#xff0c;比如面食下面&#xff0c;面条、方便面&#xff0c;面条下有干面、湿面等 一、食品种类表结构设计&#xff08;food_category&#xff09; CREATE TABLE food_category (category_id INT IDENT…...

Haply MinVerse触觉3D 鼠标—沉浸式数字操作,助力 3D 设计与仿真

在2025年CES展上&#xff0c;Haply MinVerse触觉3D鼠标凭借创新交互方式引发关注。这款设备为用户与数字环境的互动带来新维度&#xff0c;操作虚拟物体时能感受真实触觉反馈。 三维交互与触觉反馈 MinVerse 突破传统鼠标二维限制&#xff0c;增加第三运动轴&#xff0c;实现真…...

神经网络预测评估机制:损失函数详解

文章目录 一、引言二、损失函数的引入三、回顾预测算法四、损失函数的形式五、成本函数六、损失函数的定义与作用七、损失函数的重要性注释思维导图 一、引言 在上一篇文章中&#xff0c;我们了解到神经网络可通过逻辑回归等算法对输入进行预测。而判断预测结果是否准确至关重要…...

PHP实现 Apple ID 登录的服务端验证指南

在 iOS 应用中启用 “通过 Apple 登录”&#xff08;Sign In with Apple&#xff09;后&#xff0c;客户端会获取一个 身份令牌&#xff08;identity token&#xff09;。该令牌是一个JWT&#xff08;JSON Web Token&#xff09;&#xff0c;需要由服务端验证其真实性和完整性&…...

一、linux系统启动过程操作记录

一、linux系统启动过程 经历&#xff1a; 上电–>uboot–>加载内核–>挂载根文件系统–>执行应用程序 uboot等效bootloader&#xff0c;启动过程进行了 跳转到固定的位置执行相应的代码 初始化硬件设备&#xff0c;如&#xff1a;cpu初始化 &#xff0c;看门狗&a…...

【首款Armv9开源芯片“星睿“O6测评】SVE2指令集介绍与测试

SVE2指令集介绍与测试 一、什么是SVE2 在Neon架构扩展&#xff08;其指令集向量长度固定为128位&#xff09;的基础上&#xff0c;Arm设计了可伸缩向量扩展&#xff08;Scalable vector extension&#xff0c; SVE&#xff09;。SVE是一种新的单指令多数据&#xff08;SIMD&am…...

获取电脑mac地址

Windows 系统 方法1:通过命令提示符 1. 按下 `Win + R`,输入 `cmd` 后按回车,打开命令提示符。 2. 输入以下命令并按回车:...

AI核心技术与应用场景的深度解析

AI核心技术与应用场景的深度解析 在互联网大厂Java求职者的面试中&#xff0c;经常会被问到关于AI核心技术与应用场景的问题。本文通过一个故事场景来展示这些问题的实际解决方案。 第一轮提问 面试官&#xff1a;马架构&#xff0c;欢迎来到我们公司的面试现场。请问您对AI…...

练习普通话,声音细柔和

《繁星》 我爱月夜&#xff0c;但我也爱星天。从前在家乡七八月 的夜晚&#xff0c;在庭院里纳凉的时候&#xff0c;我最爱看天上密密 麻麻的繁星。望着星天&#xff0c;我就会忘记一切&#xff0c;仿佛回 到了母亲的怀里似的。 三年前在南京我住的地方&#xff0c;有…...

Linux进程详细解析

1.操作系统 概念 任何计算机系统都包含⼀个基本的程序集合&#xff0c;称为操作系统(OS)。笼统的理解&#xff0c;操作系统包括&#xff1a; • 内核&#xff08;进程管理&#xff0c;内存管理&#xff0c;文件管理&#xff0c;驱动管理&#xff09; • 其他程序&#xff08…...

Linux执行脚本报错

执行脚本报错&#xff1a;./startup.sh -bash: ./startup.sh: /bin/bash^M: bad interpreter: No such file or directory ./startup.sh -bash: ./startup.sh: /bin/bash^M: bad interpreter: No such file or directory可能的原因&#xff1a; 文件开头格式问题&#xff1a…...

C++学习:六个月从基础到就业——模板编程:类模板

C学习&#xff1a;六个月从基础到就业——模板编程&#xff1a;类模板 本文是我C学习之旅系列的第三十三篇技术文章&#xff0c;也是第二阶段"C进阶特性"的第十一篇&#xff0c;主要介绍C中的类模板编程。查看完整系列目录了解更多内容。 目录 引言类模板的基本语法…...

Conda 虚拟环境复用

文章目录 一、导出环境配置二、克隆环境配置三、区别小结 一、导出环境配置 导出&#xff1a;将当前虚拟环境导出成一个yml配置文件。conda activate your_env conda env export > your_env.yml导入&#xff1a;基于yml文件创建新环境&#xff0c;会自动按照yml里的配置&am…...

Nacos简介—4.Nacos架构和原理三

大纲 1.Nacos的定位和优势 2.Nacos的整体架构 3.Nacos的配置模型 4.Nacos内核设计之一致性协议 5.Nacos内核设计之自研Distro协议 6.Nacos内核设计之通信通道 7.Nacos内核设计之寻址机制 8.服务注册发现模块的注册中心的设计原理 9.服务注册发现模块的注册中心的服务数…...

4月27日日记

现在想来&#xff0c;可以想到什么就记录下来&#xff0c;这也是网上写日记的一个好处&#xff0c;然后 今天英语课上看到一个有关迷信的视频&#xff0c;就是老师课件里的&#xff0c;感觉画风很不错&#xff0c;但是我贫瘠的语言形容不出来&#xff0c;就想到是不是世界上的…...

CentOS7.9安装OpenSSL 1.1.1t和OpenSSH9.9p1

一、临时开启telnet登录方式&#xff0c;避免升级失败无法登录系统 &#xff08;注意telnet登录方式存在安全隐患&#xff0c;升级openssh相关服务后要记得关闭&#xff09; 1.安装telnet服务 yum -y install xinetd telnet* 2.允许root用户通过telnet登陆&#xff0c;编辑…...

单例模式:全局唯一性在软件设计中的艺术实践

引言 在软件架构设计中&#xff0c;单例模式&#xff08;Singleton Pattern&#xff09;以其独特的实例控制能力&#xff0c;成为解决资源复用与全局访问矛盾的经典方案。该模式通过私有化构造方法、静态实例存储与全局访问接口三大核心机制&#xff0c;确保系统中特定类仅存在…...

Spring 与 ActiveMQ 的深度集成实践(三)

五、实战案例分析 5.1 案例背景与需求 假设我们正在开发一个电商系统&#xff0c;其中订单模块和库存模块是两个独立的子系统 。当用户下单后&#xff0c;订单模块需要通知库存模块进行库存扣减操作 。在传统的同步调用方式下&#xff0c;订单模块需要等待库存模块完成扣减操…...

30-算法打卡-字符串-重复的子字符串-leetcode(459)-第三十天

1 题目地址 459. 重复的子字符串 - 力扣&#xff08;LeetCode&#xff09;459. 重复的子字符串 - 给定一个非空的字符串 s &#xff0c;检查是否可以通过由它的一个子串重复多次构成。 示例 1:输入: s "abab"输出: true解释: 可由子串 "ab" 重复两次构成…...

rocketmq一些异常记录

rocketmq一些异常记录 Product 设置不重复发送 发送 一次失败&#xff0c;不会在被发送到mq消息队列中&#xff0c;相当于消息丢失。 2、 Consumer 消费失败 重试三次消费 都失败 则消息消费失败&#xff0c;失败后 会放入 死信队列&#xff0c;可以手动处理在mq面板 处理死信队…...

SQLMesh 测试自动化:提升数据工程效率

在现代数据工程中&#xff0c;确保数据模型的准确性和可靠性至关重要。SQLMesh 提供了一套强大的测试工具&#xff0c;用于验证数据模型的输出是否符合预期。本文将深入探讨 SQLMesh 的测试功能&#xff0c;包括如何创建测试、支持的数据格式以及如何运行和调试测试。 SQLMesh …...

WPF使用SQLite与JSON文本文件结合存储体侧平衡数据的设计与实现

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

关系型数据库PostgreSQL vs MySQL 深度对比:专业术语+白话解析+实战案例

PostgreSQL 与 MySQL 的详细对比 PostgreSQL 和 MySQL 是两种最流行的开源关系型数据库&#xff0c;它们在设计理念、功能特性和适用场景上有显著差异。以下是它们的详细对比&#xff1a; 一、基本架构与设计理念 PostgreSQL&#xff1a;多进程架构&#xff0c;使用共享内存通…...

利用 SSRF 和 Redis 渗透

环境搭建 在本次实验中&#xff0c;我们使用 Docker 环境进行测试。 解压实验包&#xff0c;搭建 docker 环境。 docker环境 web的dockerfile 主要利用代码 &#xff1a; redis服务器 通过 docker-compose up -d 启动相关容器&#xff0c;初次启动失败。 发现 docker 版本问…...

脏读、幻读、可重复读

脏读 定义&#xff1a;一个事务读取了另一个事务尚未提交的数据 。比如事务 A 修改了某条数据但还没提交&#xff0c;此时事务 B 读取了这条被修改但未提交的数据。若事务 A 后续回滚&#xff0c;事务 B 读到的数据就是无效的&#xff0c;相当于读到了 “脏数据”。危害&#…...

第1讲、#PyTorch教学环境搭建与Tensor基础操作详解

引言 PyTorch是当前深度学习领域最流行的框架之一&#xff0c;因其动态计算图和直观的API而备受开发者青睐。本文将从零开始介绍PyTorch的环境搭建与基础操作&#xff0c;适合各种平台的用户和深度学习初学者。 1. 安装和环境搭建 macOS (Apple Silicon) 对于Mac M1/M2/M3用…...

【创新实训个人博客】数据库搭建

1.原因 为了降低模型使用以前训练的数据或者幻觉知识&#xff0c;我们在对话时需要提供相关内容的数据&#xff0c;同时由于需要最新的广告实时数据&#xff0c;实时爬取和版权问题。数据由团队在网上爬取&#xff0c;为了广告内容的有效性&#xff0c;如果长期使用&#xff0…...

《代码整洁之道》第6章 对象和数据结构 - 笔记

数据抽象 (Data Abstraction) 这个小节主要讲的是**面向对象编程&#xff08;OOP&#xff09;**的一种核心思想&#xff1a;对象应该隐藏它的内部数据&#xff0c;只暴露可以操作这些数据的“行为”&#xff08;也就是方法/函数&#xff09;。 大白话&#xff1a; 你创建一个…...

Python判断字符串中是否包含特殊字符

在 Python 中&#xff0c;判断一个字符串是否包含特殊字符可以通过多种方法实现。常见的特殊字符包括空格、感叹号、单引号、括号、星号、加号、逗号、斜杠、冒号、分号、等号、问号、 符号、方括号、花括号和 & 符号等。 为了判断字符串中是否包含这些特殊字符&#xff0…...

disruptor-spring-boot-start版本优化升级

文章目录 1.前言2.升级内容3.依赖4.总结 1.前言 由于之前写了一篇《disruptor-spring-boot-start生产实践导致pod节点CPU爆表100%的问题解决说明》的文章&#xff0c;里面说本地启动没有啥问题&#xff0c;后面我启动之前写的那个测试的controller发现&#xff0c;本地电脑的CP…...

复杂背景下无人机影像小目标检测:MPE-YOLO抗遮挡与抗背景干扰设计

目录 一、引言 二、挑战和贡献 密集小目标和遮挡 实时性要求与精度权衡 复杂背景 三、MPE-YOLO模型细节 多级特征集成器&#xff08;MFI&#xff09; 感知增强卷积&#xff08;PEC&#xff09; 增强范围C2f模块&#xff08;ES-C2f&#xff09; 四、Coovally AI模型训…...

项目实战 -- 状态管理

redux基础 还记得好久好久之前就想要实现的一个功能吗&#xff1f; 收起侧边栏折叠菜单&#xff0c;没错&#xff0c;现在才实现 因为不是父子通信&#xff0c;所以处理起来相对麻烦一点 可以使用状态树或者中间人模式 这就需要会redux了 Redux工作流&#xff1a; 异步就…...

基于单片机的智能药盒系统

标题:基于单片机的智能药盒系统 内容:1.摘要 本文聚焦于基于单片机的智能药盒系统。背景方面&#xff0c;随着人口老龄化加剧&#xff0c;老年人按时准确服药问题愈发凸显&#xff0c;同时现代快节奏生活也使人们容易遗忘服药时间。目的是设计并实现一个能帮助人们按时、按量服…...

【PyCharm- Python- ArcGIS】:安装一个和 ArcGIS 不冲突的独立 Python让PyCharm 使用 (解决全过程记录)

之前电脑上安装了anaconda3&#xff0c;python3和arcgis10.2.其中anaconda3带有python3&#xff0c;arcgis10.2自带python2.7。arcgis不能正常使用&#xff0c;之前为了使用arcgis&#xff0c;因此卸载了anaconda3和python3&#xff0c;PyCharm不能正常使用了 之前安装的卸载后…...

【C语言干货】回调函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、回调函数 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、回调函数 在 C 语言中&#xff0c;当你有一个函数并希望将其作…...