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

Three.js 搭建3D隧道监测

Three.js 搭建3D隧道监测

  • Three.js 基础元素
    • 场景scene
    • 相机carema
    • 网络模型Mesh
    • 光源light
    • 渲染器renderer
    • 控制器controls
  • 实现3d隧道监测基础
    • 实现道路
    • 实现隧道
    • 实现多个摄像头
    • 点击模型进行属性操作
    • 实现点击模型发光效果
  • 性能监视器stats
    • 引入
    • 使用
  • 总结
  • 完整代码

我们将通过three.js技术打造3d隧道监测可视化项目,隧道监测项目将涵盖照明,风机的运行情况,控制车道指示灯关闭,情报板、火灾报警告警、消防安全、车行横洞、风向仪、隧道紧急逃生出口的控制以及事故模拟等!那先来看看我们的初步成果!因为作者也是在边学习边做的情况,效果有些丑陋,希望不要见笑!!!three.js基础知识还是基本涵盖了,入门还是很有参考价值的!

在这里插入图片描述

Three.js 基础元素

我们将通过一个基本的three.js模板代码更好的概况我们的基础元素

import React, { useEffect } from 'react';
import * as THREE from 'three';
// eslint-disable-next-line import/extensions
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';export default function ThreeVisual() {// 场景let scene;// 相机let camera;// 控制器let controls;// 网络模型let mesh;// 渲染器let renderer;// debugger属性const debugObject = {light: {amlight: {color: 0xffffff,},directionalLight: {color: 0xffffff,position: {x: 0,y: 400,z: 1800,},},pointLight: {color: 0xff0000,position: {x: 0,y: 400,z: 1800,},},},};const sizes = {width: window.innerWidth,height: window.innerHeight,};useEffect(() => {// eslint-disable-next-line no-use-before-definethreeStart();}, []);const initThree = () => {const width = document.getElementById('threeMain').clientWidth;const height = document.getElementById('threeMain').clientHeight;renderer = new THREE.WebGLRenderer({antialias: true,logarithmicDepthBuffer: true,});renderer.shadowMap.enabled = true;renderer.setSize(width, height);document.getElementById('threeMain').appendChild(renderer.domElement);};const initCamera = (width, height) => {camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);camera.position.x = 0;camera.position.y = 500;camera.position.z = 1300;camera.up.x = 0;camera.up.y = 1;camera.up.z = 0;camera.lookAt({x: 0,y: 0,z: 0,});// 创建相机视锥体辅助对象// const cameraPerspectiveHelper = new THREE.CameraHelper(camera);// scene.add(cameraPerspectiveHelper);};const initScene = () => {scene = new THREE.Scene();scene.background = new THREE.Color(0xbfd1e5);};const initLight = () => {// 环境光const amlight = new THREE.AmbientLight(debugObject.light.amlight.color);amlight.position.set(1000, 1000, 1000);scene.add(amlight);};const initObject = () => {const geometry = new THREE.BoxGeometry(3000, 6, 2400);const material = new THREE.MeshBasicMaterial({color: 0xcccccc});geometry.position = new THREE.Vector3(0, 0, 0);mesh = new THREE.Mesh(geometry, [material, material, material, material, material, material]);mesh.receiveShadow = true; // cast投射,方块投射阴影scene.add(mesh);}const initControl = () => {// 将renderer关联到container,这个过程类似于获取canvas元素const pcanvas = document.getElementById('threeMain');controls = new OrbitControls(camera, pcanvas);// 如果使用animate方法时,将此函数删除// controls.addEventListener( 'change', render );// 使动画循环使用时阻尼或自转 意思是否有惯性controls.enableDamping = true;// 动态阻尼系数 就是鼠标拖拽旋转灵敏度// controls.dampingFactor = 0.25;// 是否可以缩放controls.enableZoom = true;// 是否自动旋转// controls.autoRotate = true;controls.autoRotateSpeed = 0.5;// 设置相机距离原点的最近距离// controls.minDistance  = 10;// 设置相机距离原点的最远距离controls.maxDistance = 10000;// 是否开启右键拖拽controls.enablePan = true;};function animation() {renderer.render(scene, camera);// mesh.rotateY(0.01);requestAnimationFrame(animation);}function initHelper() {const axesHelper = new THREE.AxesHelper(3000);scene.add(axesHelper);}function threeStart() {initThree();initScene();initCamera(sizes.width, sizes.height);initHelper();initObject();initLight();initControl();animation();}return <div id="threeMain" style={{ width: '100vw', height: '100vh' }} />;
}

在这里插入图片描述

场景scene

是一个三维空间,相当于我们html中的body,所有节点的容器,相当于一个空房间,承载所有的物品!所以我们定义一个全局变量scene。

初始化我们可以这样:

  const initScene = () => {scene = new THREE.Scene();scene.background = new THREE.Color(0xbfd1e5);};

相机carema

打个比方,就是你买了一个1万元的相机出门拍风景,你总是想要抓住最美的风景,那你便要调好相机最精确的位置、角度、焦距等,相机看到的内容就是我们最终在屏幕上看到的内容。在这个例子中我们用的是像我们眼睛的透视相机PerspectiveCamera。
在这里插入图片描述
还有一个常用的相机是正交相机OrthographicCamera,它看到的范围不会受距离影响!
在这里插入图片描述
我们也定义了一个全局变量camera,

初始化我们可以这样:

 const initCamera = (width, height) => {camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);camera.position.x = 0;camera.position.y = 500;camera.position.z = 1300;camera.up.x = 0;camera.up.y = 1;camera.up.z = 0;camera.lookAt({x: 0,y: 0,z: 0,});// 创建相机视锥体辅助对象// const cameraPerspectiveHelper = new THREE.CameraHelper(camera);// scene.add(cameraPerspectiveHelper);};

网络模型Mesh

在介绍它之前我们需要先了解点模型Points、线模型Line。点线面,面就是Mesh模型。点模型Points、线模型Line、网格网格模型Mesh都是由几何体Geometry和材质Material构成。在这里就不过多研究点线面了,我们最重要的知道的是一个网络模型就是一个物体穿上了衣服,没有穿衣服的皇帝不会让别人揭穿和笑话,但是我们的老板才是皇帝,所以尽量给我们的模型套件衣服吧!

同理定义一个全局变量mesh
初始化我们可以这样:

const geometry = new THREE.BoxGeometry(3000, 6, 2400);
const material = new THREE.MeshBasicMaterial({color: 0xcccccc});
geometry.position = new THREE.Vector3(0, 0, 0);
mesh = new THREE.Mesh(geometry, [material, material, material, material, material, material]);
mesh.receiveShadow = true; // cast投射,方块投射阴影
scene.add(mesh);

光源light

没有光世界便是黑暗的!同理假如没有光,摄像机看不到任何东西。所以我们需要为我们的场景加上不同光照效果。我们先从最基础的环境光AmbientLight开始。环境光意思就是哪个角度、哪个位置的光照亮度强度都一样。因为光不需要重复使用,所以我们没必要定义全局变量,所以我们初始化可以这样:

const initLight = () => {// 环境光const amlight = new THREE.AmbientLight(debugObject.light.amlight.color);amlight.position.set(1000, 1000, 1000);scene.add(amlight);
};

渲染器renderer

就相当于现实生活中你带着相机,现在去了一个美丽的地方,你需要一个相片承载下这个美丽的景色,对于threejs而言,如果你需要这张相片,就需要一个新的对象,也就是WebGL渲染器WebGLRenderer,把这些承载。
在这里插入图片描述
同理我们定义一个全局变量renderer,初始化我们可以这样:

renderer = new THREE.WebGLRenderer({... //属性配置
});

渲染器还需要补充几点,就是如何和我们的dom节点关联起来:

渲染器WebGLRenderer通过属性domElement可以获得渲染方法render()生成的Canvas画布,domElement本质上就是一个HTML元素:Canvas画布。我们也可以通过setSize()来设置尺寸。

定义一个html元素

return <div id="threeMain" style={{ width: '100vw', height: '100vh' }} />;

html元素和渲染器关联,那就给div增加一个子节点(canvas)
在这里插入图片描述

const initThree = () => {const width = document.getElementById('threeMain').clientWidth;const height = document.getElementById('threeMain').clientHeight;renderer = new THREE.WebGLRenderer({... //属性配置});renderer.setSize(width, height);  //设置画布宽高document.getElementById('threeMain').appendChild(renderer.domElement);  // 把画布加入dom节点
};

渲染器和我们的threejs元素关联, 那渲染器渲染方法.render(),把我们的场景和相机记录进来了!

renderer.render(scene, camera);

控制器controls

就是相当于可以通过我们的键盘和鼠标来控制我们的场景,使其有了交互功能!控制器种类有很多,但这里我们只说轨道控制器OrbitControls。它可以使得相机围绕目标进行轨道运动。打个比方(地球围绕太阳一样运动)。

同理我们定义一个全局变量controls,初始化我们可以这样:

controls = new OrbitControls(camera, pcanvas);

关联操作和属性介绍:

const initControl = () => {// 将renderer关联到container,这个过程类似于获取canvas元素const pcanvas = document.getElementById('threeMain');controls = new OrbitControls(camera, pcanvas);// 如果使用animate方法时,将此函数删除// controls.addEventListener( 'change', render );// 使动画循环使用时阻尼或自转 意思是否有惯性controls.enableDamping = true;// 动态阻尼系数 就是鼠标拖拽旋转灵敏度// controls.dampingFactor = 0.25;// 是否可以缩放controls.enableZoom = true;// 是否自动旋转// controls.autoRotate = true;controls.autoRotateSpeed = 0.5;// 设置相机距离原点的最近距离// controls.minDistance  = 10;// 设置相机距离原点的最远距离controls.maxDistance = 10000;// 是否开启右键拖拽controls.enablePan = true;
};

到此,我们已经把threejs基础元素介绍的差不多了,在这里还需要补充一些很容易遗漏的地方!

动画和及时更新

function animation() {controls.update()renderer.render(scene, camera);// mesh.rotateY(0.01);requestAnimationFrame(animation);
}

补充一个知识点:
requestAnimationFrame
在这里插入图片描述

实现3d隧道监测基础

实现道路

如图,我们首先实现发光这部分。
在这里插入图片描述
在这里插入图片描述
这部分主要涉及的知识是给一个平面(plane)贴图,具体的知识我在代码块相应位置已经标注。

// 图加载器
const loader = new THREE.TextureLoader();
// 加载
const texture = loader.load('/model/route.png', function(t) {// eslint-disable-next-line no-param-reassign,no-multi-assignt.wrapS = t.wrapT = THREE.RepeatWrapping; //是否重复渲染和css中的背景属性渲染方式很像t.repeat.set(1, 1);
});// 平面
const geometryRoute = new THREE.PlaneGeometry(1024, 2400);
const materialRoute = new THREE.MeshStandardMaterial({map: texture, // 使用纹理贴图side: THREE.BackSide, // 背面渲染
});
const plane = new THREE.Mesh(geometryRoute, materialRoute);
plane.receiveShadow = true;
plane.position.set(0, 8, 0);
plane.rotateX(Math.PI / 2);
scene.add(plane);

实现隧道

现在我们实现发光这部分
在这里插入图片描述
这部分主要涉及的知识是引入一个obj模型,并给模型贴上贴图(这里的材质是一个mtl)

补充知识点:

  • OBJ是一种3D模型文件,因此不包含动画、材质特性、贴图路径、动力学、粒子等信息 我们拿到一个隧道obj模型的文件打开看看,里面是什么
    在这里插入图片描述
  • mtl文件(Material Library File)是材质库文件,描述的是物体的材质信息,ASCII存储,任何文本编辑器可以将其打开和编辑。同理我们也可以打开看看,是个什么东西
    在这里插入图片描述
  • 从obj文件看出我们需要tunnelWall.mtl材质,从mtl文件,看出我们需要suidao.jpg图片(需要和模型放在同一级),其实到这里我们还是回到了引入道路的那部分,模型+贴图环节。

但是还是有一些不同的地方的,首先使用的加载器不同

const mtlLoader = new MTLLoader();
const loader = new OBJLoader(); // 在init函数中,创建loader变量,用于导入模型

其次我们的模型是属于建模自己构造的,可能你引入进来很大可能是加载不出来的!所以你需要打印对象,从中分析具体原因。

// 模型对象公共变量
const modelsObj = {tunnelWall: {mtl: '/model/tunnelWall.mtl',obj: '/model/tunnelWall.obj',mesh: null,},camera: {mtl: '/model/camera/摄像头方.mtl',obj: '/model/camera/摄像头方.obj',mesh: null,},
};
mtlLoader.load(modelsObj.tunnelWall.mtl, material => {material.preload();// 设置材质的透明度// mtl文件中的材质设置到obj加载器loader.setMaterials(material);loader.load(modelsObj.tunnelWall.obj, object => {// 设置模型大小和中心点object.children[0].geometry.computeBoundingBox();object.children[0].geometry.center();modelsObj.tunnelWall.mesh = object;scene.add(object);});
});

实现多个摄像头

现在我们实现摄像头部分
在这里插入图片描述
这里其实和实现隧道大相径庭,只不过我们是多个,而隧道是单个。所以我们需要引入组(group)和克隆(clone)的概念。

知识点补充:

  • 组对象group:相当于一个身体有胳膊、头、腿,组成一个组。每个人组合可以再次分一个组。
  • 克隆clone:字面意思就是克隆一个一模一样的你。但是需要和copy分开。
// 加载摄像头模型
const loadCameraModel = () => {const mtlLoader = new MTLLoader();const loader = new OBJLoader(); // 在init函数中,创建loader变量,用于导入模型mtlLoader.load(modelsObj.camera.mtl, material => {material.preload();// 设置材质的透明度// mtl文件中的材质设置到obj加载器loader.setMaterials(material);loader.load(modelsObj.camera.obj, object => {console.log(object);// 设置模型大小object.children[0].geometry.computeBoundingBox();object.children[0].geometry.center();modelsObj.camera.mesh = object;cloneCameraModel(4, 60, 180);cloneCameraModel(4, -200, 180);});});
};
// 克隆摄像头模型
const cloneCameraModel = (cameraSize, lrInterval, baInterval) => {const group = new THREE.Group();for (let i = 0; i <= cameraSize; i += 1) {modelsObj[`camera${i}`] = modelsObj.camera.mesh.clone();modelsObj[`camera${i}`].position.set(lrInterval, 180, baInterval * (i % 2 === 0 ? -i : i));modelsObj[`camera${i}`].scale.set(1, 1, 1);group.add(modelsObj[`camera${i}`])}scene.add(group);
};

点击模型进行属性操作

这块我们需要涉及的知识点是点击操作(Raycaster)、发光部分(效果合成器,shader渲染使用)、debugger模式(gui)

首先我们实现对模型进行的点击,我们需要使用raycaster 定义全局变量mouse初始化鼠标,光线追踪。可以这样定义:

// 获取鼠标坐标 处理点击某个模型的事件
const mouse = new THREE.Vector2(); // 初始化一个2D坐标用于存储鼠标位置
const raycaster = new THREE.Raycaster(); // 初始化光线追踪

知识点补充:

光线投射raycaster:可以向特定方向投射光线,并测试哪些对象与其相交,由鼠标点击转为世界坐标的过程。就是把一个2d坐标转变成3d坐标的强大类!

我们监听屏幕点击事件

const pcanvas = document.getElementById('threeMain');
// 监听点击事件,pcanvas
pcanvas.addEventListener('click', e => onmodelclick(e)); // 监听点击

计算点击坐标,屏幕坐标系转换成世界坐标系的过程。并赋值全局变量点击模型clickModel。

const onmodelclick = event => {console.log(event);// 获取鼠标点击位置mouse.x = (event.clientX / sizes.width) * 2 - 1;mouse.y = -(event.clientY / sizes.height) * 2 + 1;console.log(mouse);raycaster.setFromCamera(mouse, camera);const intersects = raycaster.intersectObjects(scene.children); // 获取点击到的模型的数组,从近到远排列// const worldPosition = new THREE.Vector3(); // 初始化一个3D坐标,用来记录模型的世界坐标if (intersects.length > 0) {clickModel = intersects[0].object; outlinePass.selectedObjects = [];outlinePass.selectedObjects = [clickModel];}
};

实现点击模型发光效果

threejs提供了一个扩展库EffectComposer.js,通过这个我们可以实现一些后期处理效果。所谓后期处理,就像ps一样,对threejs的渲染结果进行后期处理,比如添加发光效果。我们结合高亮发光描边可以实现下图发光效果。
在这里插入图片描述

  • 引入相关类
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader';
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass';
  • 初始化三个全局变量
let composer;
let effectFXAA;
let outlinePass;
  • 赋值选中发光模型
const onmodelclick = event => {
...if (intersects.length > 0) {outlinePass.selectedObjects = [];outlinePass.selectedObjects = [clickModel];}
};
  • 初始化加载发光效果
// 效果合成器,shader渲染使用
const initEffectComposer = () => {// 处理模型闪烁问题【优化展示网格闪烁】// const parameters = { format: THREE.RGBAFormat };// const size = renderer.getDrawingBufferSize(new THREE.Vector2());// const renderTarget = new THREE.WebGLMultipleRenderTargets(size.width, size.height, parameters);composer = new EffectComposer(renderer);const renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);outlinePass = new OutlinePass(new THREE.Vector2(sizes.width, sizes.height), scene, camera);outlinePass.visibleEdgeColor.set(255, 255, 0);outlinePass.edgeStrength = 1.0; // 边框的亮度outlinePass.edgeGlow = 1; // 光晕[0,1]outlinePass.usePatternTexture = false; // 是否使用父级的材质outlinePass.edgeThickness = 1.0; // 边框宽度outlinePass.downSampleRatio = 1; // 边框弯曲度composer.addPass(outlinePass);const outputPass = new OutputPass();composer.addPass(outputPass);effectFXAA = new ShaderPass(FXAAShader);effectFXAA.uniforms.resolution.value.set(1 / sizes.width, 1 / sizes.height);composer.addPass(effectFXAA);
};
  • 渲染循环执行
function animation() {stats.update();renderer.render(scene, camera);composer.render();// mesh.rotateY(0.01);requestAnimationFrame(animation);
}

debugger模式 这节主要涉及gui,并且补充一下阴影的知识。gui是一个图形用户界面工具,我们可以通过这个工具实现对属性进行动态的操作,很方便。下面标红的就是我们的界面工具
在这里插入图片描述

我们通过增加点光源来举个例子。

  • 首先我们初始化全局变量gui并且赋值
// debugger
let gui;
function initDebugger() {gui = new GUI();
}
  • 定义全局变量debugObject需要改变的属性。
// debugger属性
const debugObject = {light: {pointLight: {color: 0xff0000,position: {x: 0,y: 400,z: 1800,},},},
};
  • 定义点光源,对点光源的位置和颜色属性动态切换
// 点光源
const pointLight = new THREE.PointLight(debuggerPointLight.color, 1);
pointLight.castShadow = true;
pointLight.position.set(100, 100, 300);
scene.add(pointLight);
const pointLightFolder = lightFolder.addFolder('点光源');
pointLightFolder.addColor(debuggerPointLight, 'color').onChange(function(value) {pointLight.color.set(value);
});
// 点光源位置
pointLightFolder.add(debuggerPointLight.position, 'x', -1000, 1000).onChange(function(value) {pointLight.position.x = value;pointLightHelper.update();
});
pointLightFolder.add(debuggerPointLight.position, 'y', -1000, 1000).onChange(function(value) {pointLight.position.y = value;pointLightHelper.update();
});
pointLightFolder.add(debuggerPointLight.position, 'z', -1000, 1000).onChange(function(value) {pointLight.position.z = value;pointLightHelper.update();
});

实现效果如图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 开启阴影

阴影渲染

renderer = new THREE.WebGLRenderer({...
});
renderer.shadowMap.enabled = true;

点光源投射光影

const pointLight = new THREE.PointLight(debuggerPointLight.color, 1);
pointLight.castShadow = true;

模型和道路接受阴影和投射阴影

plane.receiveShadow = true;
loader.load(modelsObj.tunnelWall.obj, object => {object.traverse(obj => {if (obj.castShadow !== undefined) {// 开启投射影响// eslint-disable-next-line no-param-reassignobj.castShadow = true;// 开启被投射阴影// eslint-disable-next-line no-param-reassignobj.receiveShadow = true;}});

性能监视器stats

一个计算渲染分辨率FPS的工具,在这里提一下。
在这里插入图片描述

引入

import Stats from 'three/examples/jsm/libs/stats.module';

使用

// 性能监视器
let stats;document.getElementById('threeMain').appendChild(stats.domElement);function initStats() {stats = new Stats();stats.showPanel(1); // 0: fps, 1: ms, 2: mb, 3+: custom
}function animation() {stats.update();renderer.render(scene, camera);composer.render();// mesh.rotateY(0.01);requestAnimationFrame(animation);
}

总结

这是我们实现目标的一个小小起点,属于冰山一角,前路漫漫,还需要阅读很多知识文档和试错阶段,如果你对后续感兴趣的话,可以跟进一下呀!谢谢!

完整代码

import React, { useEffect } from 'react';
import * as THREE from 'three';
// eslint-disable-next-line import/extensions
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// eslint-disable-next-line import/extensions
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
import Stats from 'three/examples/jsm/libs/stats.module';
// eslint-disable-next-line import/extensions
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader';
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass';export default function ThreeVisual() {// 场景let scene;// 相机let camera;// 控制器let controls;// 网络模型let mesh;// 渲染器let renderer;// 性能监视器let stats;// debuggerlet gui;// 当前点击模型let clickModel;// 当前点击需要使用的let composer;let effectFXAA;let outlinePass;// debugger属性const debugObject = {light: {amlight: {color: 0xffffff,},directionalLight: {color: 0xffffff,position: {x: 0,y: 400,z: 1800,},},pointLight: {color: 0xff0000,position: {x: 0,y: 400,z: 1800,},},},model: {wall: {position: {x: 0,y: 210,z: 0,},scale: 0.12,opacity: {wallTopOpa: 0.4,wallSideOpa: 1,},},camera: {position: {x: 100,y: 100,z: 100,},scale: 1,},},};// 模型对象const modelsObj = {tunnelWall: {mtl: '/model/tunnelWall.mtl',obj: '/model/tunnelWall.obj',mesh: null,},camera: {mtl: '/model/camera/摄像头方.mtl',obj: '/model/camera/摄像头方.obj',mesh: null,},};const sizes = {width: window.innerWidth,height: window.innerHeight,};// 获取鼠标坐标 处理点击某个模型的事件const mouse = new THREE.Vector2(); // 初始化一个2D坐标用于存储鼠标位置const raycaster = new THREE.Raycaster(); // 初始化光线追踪useEffect(() => {// eslint-disable-next-line no-use-before-definethreeStart();}, []);const initThree = () => {const width = document.getElementById('threeMain').clientWidth;const height = document.getElementById('threeMain').clientHeight;renderer = new THREE.WebGLRenderer({antialias: true,logarithmicDepthBuffer: true,});renderer.shadowMap.enabled = true;renderer.setSize(width, height);document.getElementById('threeMain').appendChild(renderer.domElement);// renderer.setClearColor(0xFFFFFF, 1.0);document.getElementById('threeMain').appendChild(stats.domElement);};const initCamera = (width, height) => {camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);camera.position.x = 0;camera.position.y = 500;camera.position.z = 1300;camera.up.x = 0;camera.up.y = 1;camera.up.z = 0;camera.lookAt({x: 0,y: 0,z: 0,});// 创建相机视锥体辅助对象// const cameraPerspectiveHelper = new THREE.CameraHelper(camera);// scene.add(cameraPerspectiveHelper);};const initScene = () => {scene = new THREE.Scene();scene.background = new THREE.Color(0xbfd1e5);};const initLight = () => {const lightFolder = gui.addFolder('光');const {directionalLight: debuggerDirectionalLight,pointLight: debuggerPointLight,} = debugObject.light;// 环境光// const amlight = new THREE.AmbientLight(debugObject.light.amlight.color);// amlight.position.set(1000, 1000, 1000);// scene.add(amlight);// // 环境光debugger// const amlightFolder=lightFolder.addFolder("环境光")// amlightFolder.addColor(debugObject.light.amlight, 'color').onChange(function(value){//   amlight.color.set(value);// });// 平行光// 创建平行光,颜色为白色,强度为 10const directionalLight = new THREE.DirectionalLight(debuggerDirectionalLight.color, 1);// 设置平行光的方向directionalLight.position.set(0, 400, 1000);directionalLight.castShadow = true;const directonalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 20);// scene.add(directonalLightHelper);scene.add(directionalLight);// 平行光debuggerconst directionalLightFolder = lightFolder.addFolder('平行光');directionalLightFolder.addColor(debuggerDirectionalLight, 'color').onChange(function(value) {directionalLight.color.set(value);});// 平行光位置directionalLightFolder.add(debuggerDirectionalLight.position, 'x', -1000, 1000).onChange(function(value) {directionalLight.position.x = value;directonalLightHelper.update();});directionalLightFolder.add(debuggerDirectionalLight.position, 'y', -1000, 1000).onChange(function(value) {directionalLight.position.y = value;directonalLightHelper.update();});directionalLightFolder.add(debuggerDirectionalLight.position, 'z', -1000, 1000).onChange(function(value) {directionalLight.position.z = value;directonalLightHelper.update();});// 点光源const pointLight = new THREE.PointLight(debuggerPointLight.color, 1);pointLight.castShadow = true;pointLight.position.set(100, 100, 300);const sphereSize = 10;const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);scene.add(pointLight);scene.add(pointLightHelper);const pointLightFolder = lightFolder.addFolder('点光源');pointLightFolder.addColor(debuggerPointLight, 'color').onChange(function(value) {pointLight.color.set(value);});// 点光源位置pointLightFolder.add(debuggerPointLight.position, 'x', -1000, 1000).onChange(function(value) {pointLight.position.x = value;pointLightHelper.update();});pointLightFolder.add(debuggerPointLight.position, 'y', -1000, 1000).onChange(function(value) {pointLight.position.y = value;pointLightHelper.update();});pointLightFolder.add(debuggerPointLight.position, 'z', -1000, 1000).onChange(function(value) {pointLight.position.z = value;pointLightHelper.update();});};const initObject = () => {const geometry = new THREE.BoxGeometry(3000, 6, 2400);const loader = new THREE.TextureLoader();const texture = loader.load('/model/route.png', function(t) {// eslint-disable-next-line no-param-reassign,no-multi-assignt.wrapS = t.wrapT = THREE.RepeatWrapping;t.repeat.set(1, 1);});const material = new THREE.MeshBasicMaterial({ color: 0xcccccc });geometry.position = new THREE.Vector3(0, 0, 0);mesh = new THREE.Mesh(geometry, [material, material, material, material, material, material]);mesh.receiveShadow = true; // cast投射,方块投射阴影scene.add(mesh);// 平面const geometryRoute = new THREE.PlaneGeometry(1024, 2400);const materialRoute = new THREE.MeshStandardMaterial({map: texture, // 使用纹理贴图side: THREE.BackSide, // 两面都渲染});const plane = new THREE.Mesh(geometryRoute, materialRoute);plane.receiveShadow = true;plane.position.set(0, 8, 0);plane.rotateX(Math.PI / 2);scene.add(plane);};const initControl = () => {// 将renderer关联到container,这个过程类似于获取canvas元素const pcanvas = document.getElementById('threeMain');controls = new OrbitControls(camera, pcanvas);// 如果使用animate方法时,将此函数删除// controls.addEventListener( 'change', render );// 使动画循环使用时阻尼或自转 意思是否有惯性controls.enableDamping = true;// 动态阻尼系数 就是鼠标拖拽旋转灵敏度// controls.dampingFactor = 0.25;// 是否可以缩放controls.enableZoom = true;// 是否自动旋转// controls.autoRotate = true;controls.autoRotateSpeed = 0.5;// 设置相机距离原点的最近距离// controls.minDistance  = 10;// 设置相机距离原点的最远距离controls.maxDistance = 10000;// 是否开启右键拖拽controls.enablePan = true;};const onmodelclick = event => {console.log(event);// 获取鼠标点击位置mouse.x = (event.clientX / sizes.width) * 2 - 1;mouse.y = -(event.clientY / sizes.height) * 2 + 1;console.log(mouse);raycaster.setFromCamera(mouse, camera);const intersects = raycaster.intersectObjects(scene.children); // 获取点击到的模型的数组,从近到远排列// const worldPosition = new THREE.Vector3(); // 初始化一个3D坐标,用来记录模型的世界坐标if (intersects.length > 0) {clickModel = intersects[0].object;outlinePass.selectedObjects = [];outlinePass.selectedObjects = [clickModel];// intersects[0].object.getWorldPosition(worldPosition); // 将点中的3D模型坐标记录到worldPosition中// const texture = new THREE.TextureLoader().load("/model/route.png");// const spriteMaterial = new THREE.SpriteMaterial({//   map: texture,// 设置精灵纹理贴图// });// const sprite = new THREE.Sprite(spriteMaterial); // 精灵模型,不管从哪个角度看都可以一直面对你// scene.add(sprite);// sprite.scale.set(40,40,40);// sprite.position.set(worldPosition.x, worldPosition.y + 8, worldPosition.z); // 根据刚才获取的世界坐标设置精灵模型位置,高度加了3,是为了使精灵模型显示在点击模型的上方}};const initEvent = () => {window.addEventListener('resize', () => {// Update sizessizes.width = window.innerWidth;sizes.height = window.innerHeight;// Update cameracamera.aspect = sizes.width / sizes.height;camera.updateProjectionMatrix();// Update rendererrenderer.setSize(sizes.width, sizes.height);composer.setSize(sizes.width, sizes.height);renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));effectFXAA.uniforms.resolution.value.set(1 / sizes.width, 1 / sizes.height);});const pcanvas = document.getElementById('threeMain');// 监听点击事件pcanvas.addEventListener('click', e => onmodelclick(e)); // 监听点击};const loadModel = () => {const mtlLoader = new MTLLoader();const loader = new OBJLoader(); // 在init函数中,创建loader变量,用于导入模型mtlLoader.load(modelsObj.tunnelWall.mtl, material => {material.preload();// 设置材质的透明度// mtl文件中的材质设置到obj加载器loader.setMaterials(material);loader.load(modelsObj.tunnelWall.obj, object => {object.traverse(obj => {if (obj.castShadow !== undefined) {// 开启投射影响// eslint-disable-next-line no-param-reassignobj.castShadow = true;// 开启被投射阴影// eslint-disable-next-line no-param-reassignobj.receiveShadow = true;}});// 设置模型大小object.children[0].geometry.computeBoundingBox();object.children[0].geometry.center();// debugger模型属性const { scale, position, opacity } = debugObject.model.wall;// 模型本有属性const {scale: changeScale,position: changePositon,material: changeMaterial,} = object.children[0];changeScale.set(scale, scale, scale);changePositon.set(position.x, position.y, position.z);changeMaterial[0].transparent = true;changeMaterial[0].opacity = opacity.wallTopOpa;changeMaterial[1].transparent = true;changeMaterial[1].opacity = opacity.wallSideOpa;modelsObj.tunnelWall.mesh = object;scene.add(object);// 模型debuggerconst modelFolder = gui.addFolder('模型');const wallFolder = modelFolder.addFolder('墙');wallFolder.add(position, 'x', -100, 300).step(0.5).onChange(function(value) {changePositon.x = value;});wallFolder.add(position, 'y', -100, 300).step(0.5).onChange(function(value) {changePositon.y = value;});wallFolder.add(position, 'z', -100, 300).step(0.5).onChange(function(value) {changePositon.z = value;});wallFolder.add(debugObject.model.wall, 'scale', 0.01, 0.3).step(0.001).onChange(function(value) {changeScale.set(value, value, value);});wallFolder.add(opacity, 'wallTopOpa', 0, 1).step(0.01).onChange(function(value) {changeMaterial[0].opacity = value;});wallFolder.add(opacity, 'wallSideOpa', 0, 1).step(0.01).onChange(function(value) {changeMaterial[1].opacity = value;});});});};// 克隆摄像头模型const cloneCameraModel = (cameraSize, lrInterval, baInterval) => {const group = new THREE.Group();for (let i = 0; i <= cameraSize; i += 1) {modelsObj[`camera${i}`] = modelsObj.camera.mesh.clone();modelsObj[`camera${i}`].position.set(lrInterval, 180, baInterval * (i % 2 === 0 ? -i : i));modelsObj[`camera${i}`].scale.set(1, 1, 1);group.add(modelsObj[`camera${i}`])}scene.add(group);};// 加载摄像头模型const loadCameraModel = () => {const mtlLoader = new MTLLoader();const loader = new OBJLoader(); // 在init函数中,创建loader变量,用于导入模型mtlLoader.load(modelsObj.camera.mtl, material => {material.preload();// 设置材质的透明度// mtl文件中的材质设置到obj加载器loader.setMaterials(material);loader.load(modelsObj.camera.obj, object => {object.traverse(obj => {if (obj.castShadow !== undefined) {// 开启投射影响// eslint-disable-next-line no-param-reassignobj.castShadow = true;// 开启被投射阴影// eslint-disable-next-line no-param-reassignobj.receiveShadow = true;}});console.log(object);// 设置模型大小object.children[0].geometry.computeBoundingBox();object.children[0].geometry.center();// debugger模型属性object.children[0].scale.set(1, 1, 1);object.children[0].position.set(100, 100, 100);modelsObj.camera.mesh = object;cloneCameraModel(4, 60, 180);cloneCameraModel(4, -200, 180);});});};// 效果合成器,shader渲染使用const initEffectComposer = () => {// 处理模型闪烁问题【优化展示网格闪烁】// const parameters = { format: THREE.RGBAFormat };// const size = renderer.getDrawingBufferSize(new THREE.Vector2());// const renderTarget = new THREE.WebGLMultipleRenderTargets(size.width, size.height, parameters);composer = new EffectComposer(renderer);const renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);outlinePass = new OutlinePass(new THREE.Vector2(sizes.width, sizes.height), scene, camera);outlinePass.visibleEdgeColor.set(255, 255, 0);outlinePass.edgeStrength = 1.0; // 边框的亮度outlinePass.edgeGlow = 1; // 光晕[0,1]outlinePass.usePatternTexture = false; // 是否使用父级的材质outlinePass.edgeThickness = 1.0; // 边框宽度outlinePass.downSampleRatio = 1; // 边框弯曲度composer.addPass(outlinePass);const outputPass = new OutputPass();composer.addPass(outputPass);effectFXAA = new ShaderPass(FXAAShader);effectFXAA.uniforms.resolution.value.set(1 / sizes.width, 1 / sizes.height);composer.addPass(effectFXAA);};function animation() {stats.update();renderer.render(scene, camera);composer.render();// mesh.rotateY(0.01);requestAnimationFrame(animation);}function initHelper() {// const axesHelper = new THREE.AxesHelper(3000);// scene.add(axesHelper);}function initStats() {stats = new Stats();stats.showPanel(1); // 0: fps, 1: ms, 2: mb, 3+: custom}function initDebugger() {gui = new GUI();}function threeStart() {initEvent();initStats();initDebugger();initThree();initScene();initCamera(sizes.width, sizes.height);initHelper();initLight();initControl();initObject();loadModel();loadCameraModel();initEffectComposer();animation();}return <div id="threeMain" style={{ width: '100vw', height: '100vh' }} />;
}

相关文章:

Three.js 搭建3D隧道监测

Three.js 搭建3D隧道监测 Three.js 基础元素场景scene相机carema网络模型Mesh光源light渲染器renderer控制器controls 实现3d隧道监测基础实现道路实现隧道实现多个摄像头点击模型进行属性操作实现点击模型发光效果 性能监视器stats引入使用 总结完整代码 我们将通过three.js技…...

「IDE」集成开发环境专栏目录大纲

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「IDE」集成开发环境&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定…...

MySQL-初识数据库

目录 一、数据库基础概念 1、SQL 2、数据&#xff08;Data&#xff09; 3、数据库&#xff08;DB&#xff09; 4、数据库管理系统DBMS 5、数据库系统DBS 6、关系模型&#xff08;Relational Model&#xff09; 7、E-R图 8、常见的数据库 9、数据库基本操作 一、数据库…...

初始 html

html 文件结构 html 标签是整个 html 文件的根标签(最顶层标签) head 标签中写页面的属性. body 标签中写的是页面上显示的内容 title 标签中写的是页面的标题 <html><head><title>这是一个标题</title></head><body></body> <…...

前端 call、bind、apply的实际使用

目录 一、call 1、继承的子类可以使用父类的方法 2、可以接收任意参数 二、call、apply、bind比较 1、案例一 2、案例二 三、总结 这个三个方法都是改变函数的this指向的方法。 一、call 代码&#xff1a; const obj{uname:"pink"}function fn(){console.log…...

非关系型数据库NoSQL的类型与优缺点对比

NoSQL数据库根据数据模型和应用场景主要分为四种类型&#xff1a;键值型、列族型、文档型和图形型。以下是对每种类型的详细描述&#xff0c;包括其应用场景、优缺点的比较&#xff1a; 1. 键值型数据库 (Key-Value Store) 典型代表 RedisMemcachedAmazon DynamoDB 应用场景…...

面试击穿mysql

Mysql三大范式: 第一范式&#xff08;1NF&#xff09;&#xff1a; 不符合第一范式的典型情况是在一个字段中存放多种不同类型的详细信息。例如&#xff0c;在商品表中&#xff0c;若将商品名称、价格和类型都存储在同一个字段中&#xff0c;会带来诸多弊端。首先&#xff0c;在…...

PyQt5超详细教程终篇

PyQt5超详细教程 前言 接&#xff1a; [【Python篇】PyQt5 超详细教程——由入门到精通&#xff08;序篇&#xff09;](【Python篇】PyQt5 超详细教程——由入门到精通&#xff08;序篇&#xff09;-CSDN博客) 建议把代码复制到pycahrm等IDE上面看实际效果&#xff0c;方便理…...

Android OpenGL ES详解——纹理:纹理过滤GL_NEAREST和GL_LINEAR的区别

目录 一、概念 1、纹理过滤 2、邻近过滤 3、线性过滤 二、邻近过滤和线性过滤的区别 三、源码下载 一、概念 1、纹理过滤 当纹理被应用到三维物体上时&#xff0c;随着物体表面的形状和相机视角的变化&#xff0c;会导致纹理在渲染过程中出现一些问题&#xff0c;如锯齿…...

Elasticsearch实战应用:从入门到精通

在当今这个数据爆炸的时代&#xff0c;如何快速、有效地从海量数据中检索信息&#xff0c;已经成为了许多企业和开发者面临的挑战。Elasticsearch&#xff0c;作为一个基于Lucene的搜索引擎&#xff0c;以其强大的全文搜索能力、分布式特性以及易用性&#xff0c;成为了解决这一…...

axios平替!用浏览器自带的fetch处理AJAX(兼容表单/JSON/文件上传)

fetch 是啥&#xff1f; fetch 函数是 JavaScript 中用于发送网络请求的内置 API&#xff0c;可以替代传统的 XMLHttpRequest。它可以发送 HTTP 请求&#xff08;如 GET、POST 等&#xff09;&#xff0c;并返回一个 Promise&#xff0c;从而简化异步操作 基本用法 /* 下面是…...

【优选算法 — 滑动窗口】水果成篮 找到字符串中所有字母异位词

水果成篮 水果成篮 题目描述 因为只有两个篮子&#xff0c;每个篮子装的水果种类相同&#xff0c;如果从 0 开始摘&#xff0c;则只能摘 0 和 1 两个种类 &#xff1b; 因为当我们在两个果篮都装有水果的情况下&#xff0c;如果再走到下一颗果树&#xff0c;果树的水果种类…...

Go 数据库查询与结构体映射

下面是关于如何使用 Go 进行数据库查询并映射数据到结构体的教程&#xff0c;重点讲解 结构体字段导出 和 db 标签 的使用。 Go 数据库查询与结构体映射教程 在 Go 中&#xff0c;我们可以使用 database/sql 或 sqlx 等库与数据库进行交互。为了方便地将数据库查询结果映射到结…...

Wi-Fi背后的工作原理与技术发展历程介绍【无线通信小百科】

1个视频说清楚WIFI&#xff1a;频段/历程/技术参数/常用模块 智能手机拥有率越来越高的今天&#xff0c;大家已经习惯了通过无线网络上网的方式。除了在外面需要用手机流量&#xff0c;我们通常在家里或者机场&#xff0c;商场都可以通过Wi-Fi连接上网。本期文章将为大家介绍Wi…...

2024 年(第 7 届)“泰迪杯”数据分析技能赛B 题 特殊医学用途配方食品数据分析 完整代码 结果 可视化分享

一、背景特殊医学用途配方食品简称特医食品&#xff0c;是指为满足进食受限、消化吸收障碍、代谢素乱或者特定疾病状态人群对营养素或者膳食的特殊需要&#xff0c;专门加工配置而成的配方食品&#xff0c;包括0月龄至12月龄的特殊医学用途婴儿配方食品和适用于1岁以上的特殊医…...

STM32学习笔记------编程驱动蜂鸣器实现音乐播放

1. 硬件准备 STM32开发板&#xff1a;STM32F407系列蜂鸣器&#xff1a;常见的蜂鸣器分为两类&#xff1a;有源蜂鸣器和无源蜂鸣器。若使用有源蜂鸣器&#xff0c;只需提供电源和控制信号即可&#xff1b;若使用无源蜂鸣器&#xff0c;则需要控制频率。外接电源&#xff08;可选…...

ubuntu18.04 安装与卸载NCCL conda环境安装PaddlePaddle

cuda版本11.2 说明PaddlePaddle需要安装NCCL 1、Log in | NVIDIA Developer 登录官网 找到对应版本 官方提供了多种安装方式&#xff0c;本文使用Local installers (x86)本地安装 点击对应的版本下载如&#xff1a; nccl-local-repo-ubuntu1804-2.8.4-cuda11.2_1.0-1_amd6…...

AI有鼻子了,还能远程传输气味,图像生成香水

众所周知&#xff0c;图像、音乐能用AI生成&#xff0c;但出乎意料的是&#xff0c;气味也行。最近&#xff0c;一个名叫Osmo的初创公司宣布&#xff0c;他们成功地将气味数字化了。第一个成功的案例是“新鲜的夏季李子”&#xff0c;而且复现出的味道“闻起来”很不错。整个过…...

学习配置dify过程记录

最近在学习安装 Dify 并集成 Ollama 和 Xinference&#xff0c;学习过程中遇到很多问题&#xff0c;所以我都记录下来。 本人电脑环境&#xff1a;MacBook Pro 15.1系统 基本是基于B站教程一步步搭建: 【Dify快速入门 | 本地部署Dify基于Llama 3.1和OpenAI创建聊天机器人与知…...

简易抽奖器源码以及打包操作

import wx import random import time# 定义Myframe类,继承Frame class Myframe(wx.Frame):# 奖品rewards [桥本香奈, 二代CC, NaNa, 情深叉]# 构造方法def __init__(self):# 父类初始化super().__init__(None, title主界面, size(500, 400), pos(500, 200))# 创建面板&#x…...

一文了解什么是腾讯云开发

一文了解什么是腾讯云开发 关于云开发的猜想腾讯云开发腾讯云开发的优势无服务跨平台轻松托管节约成本 快速上手云开发环境快速搭建管理后台 云开发体验 关于云开发的猜想 说到云开发&#xff0c;作为开发者的大家是否大概就有了想法。比如说过去的开发工作都是在自己本地电脑…...

[CKS] K8S NetworkPolicy Set Up

最近准备花一周的时间准备CKS考试&#xff0c;在准备考试中发现有一个题目关于不安全项目修复的题目。 ​ 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博客[CKS] K8S Ne…...

【JAVA】Java基础—面向对象编程:构造方法-实现一个Car类,包含多个构造方法,创建不同的汽车对象

在Java中&#xff0c;构造方法则是用于创建对象的特殊方法。通过构造方法&#xff0c;可以在创建对象时初始化其属性。构造方法的重载允许我们根据不同的需求定义多个构造方法&#xff0c;从而灵活地创建对象。 我们可以将汽车的构造方法比作汽车的配置选项。比如&#xff0c;…...

初识网络编程TCP/IP

目录 前言相关名词解释应用层协议——HTTP传输层协议socketTCP帧头格式三次握手、四次挥手 UDPTCP的socket实现 参考博文 前言 刚碰到网络编程&#xff0c;会出现一堆协议、概念、这层次那技术的&#xff0c;头都大了&#xff0c;还是得总结总结…… 相关名词解释 ✨✨网络…...

快速入门Zookeeper

Zookeeper ZooKeeper作为一个强大的开源分布式协调服务&#xff0c;扮演着分布式系统中至关重要的角色。它提供了一个中心化的服务&#xff0c;用于维护配置信息、命名、提供分布式同步以及提供组服务等。通过其高性能和可靠的特性&#xff0c;ZooKeeper能够确保在复杂的分布式…...

Filter and Search 筛选和搜索

Goto Data Grid 数据网格 Filter and Search 筛选和搜索 Filter Drop-down Menus (Excel-style) 筛选器下拉菜单&#xff08;Excel 样式&#xff09; 要调用列的筛选器下拉菜单&#xff0c;请单击列标题中的筛选器图标。在 “Values” 选项卡中&#xff0c;用户可以从 Data …...

spark的学习-06

SparkSQL读写数据的方式 1&#xff09;输入Source 方式一&#xff1a;给定读取数据源的类型和地址 spark.read.format("json").load(path) spark.read.format("csv").load(path) spark.read.format("parquet").load(path) 方式二&#xff1a…...

Linux C/C++ Socket 编程

本文目录 Linux C语言 socket 编程 client 端头文件 unistd.h & arpa/inet.h1. **unistd.h**2. **arpa/inet.h** socket() 创建套接字sockaddr_in 结构体inet_pton()connect()send()recv()send() 和 recv() 中的 flags 参数**默认行为&#xff08;flags 0&#xff09;的特…...

Flutter错误: uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared

前言 今天要做蓝牙通信的功能&#xff0c;我使用了flutter_reactive_ble这个库&#xff0c;但是在运行的时候发现一下错误 Launching lib/main.dart on AQM AL10 in debug mode... /Users/macbook/Desktop/test/flutter/my_app/android/app/src/debug/AndroidManifest.xml Err…...

Spark 的容错机制:保障数据处理的稳定性与高效性

Spark 的介绍与搭建&#xff1a;从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交&#xff1a;本地与集群模式全解析-CSDN博客 Spark on YARN&#xff1a;Spark集群模式…...

盐城哪有做网站建设的/如何让百度搜索到自己的网站

[索引页][源码下载]再接再厉VS 2008 sp1 .NET 3.5 sp1(1) - Entity Framework(实体框架)之添加、查询、更新和删除的Demo作者&#xff1a;webabcd介绍以Northwind为示例数据库&#xff0c;ADO.NET Entity Framework之完全面向对象的添加操作、查询操作、更新操作和删除操作示例…...

南宁制作网站/百度灰色关键词排名技术

CentOS7设置静态IP、搭建单机版FastDFS图片服务器、使用FastDFS-Client客户端进行简单测试、实现图片上传、实现商品添加修改删除 CentOS7设置静态IP而且还可以上网 192.168.173.148 但是用mac的终端远程连接到CentOS的时候&#xff0c;由于家里和学校IP不同&#xff0c;虚拟…...

湖北营销型网站建设/网站的seo是什么意思

数据流分类&#xff1a;流序列中的数据既可以是未经加工的原始二进制数据&#xff0c;也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种&#xff1a;1) 字节流&#xff1a;数据流中最小的数据单元是字节2) 字符流&#xff1a;数据流中最小的数据单…...

关于做好学院网站建设的要求/百度学术论文查重官网入口

本文在研发组织管理中的位置&#xff1a; 研发组织管理--项目管理--敏捷迭代管理--每日站会 目的 1、同步信息&#xff0c;抛出问题&#xff1b;2、周知需求变更及调整&#xff1b; 时间&频次 1、每天一次固定时间, 开始时间由master安排&#xff1b;2、时长由master控制…...

赤壁网站建设/如何去推广一个app

前言 今天是2022年12月31日&#xff0c;今年的最后一天&#xff0c;年关已至。 又到了&#xff1a;回头看路&#xff0c;低头赶路&#xff0c;抬头望路的时候。 回顾2022 疫情中的2022 今年应该算是疫情的高峰期吧&#xff0c;各种新冠变异株横行&#xff0c;从严控到一夜…...

网站建设都用什么软件/厦门网站制作全程服务

美国存在主义心理学之父罗洛梅 克尔凯郭尔&#xff08;Soren Kierkegaard&#xff09;开山鼻祖海德格尔&#xff08;Martin Heidegger&#xff09; 大师戈德斯坦&#xff08;Kurt Goldstein&#xff09; 提出了关于自我实现、焦虑和恐惧的观点罗洛梅书籍 《咨询的艺术&#xff…...