前端前沿web 3d可视化技术 ThreeJS学习全记录

跳动的世界线 2024-06-12 15:33:05 阅读 87

前端前沿web 3d可视化技术

完整案例与项目代码:gitee开源项目地址 https://gitee.com/jumping-world-line/01_threeJS_basic

随着浏览器性能和网络带宽的提升 使得3D技术不再是桌面的专利 打破传统平面展示模式

前端方向主要流向的3D图形库包括Three.js和WebGL

WebGL灵活高性能,但代码量大,难度大,需要掌握很多底层知识和数学知识Threejs封装了WebGL的底层细节,可拓展性强,有很多开源的插件和工具,更易上手

就像2G时代文字信息是主要传输媒介 3G时代的图片 4G时代的视频

随着硬件性能与技术的提升,未来的前端也一定是3D的

开始学习

threejs官方项目

首先要有一定的前端开发基础以及开发环境 不多赘述

访问https://threejs.org/ 点击github

在这里插入图片描述

在这里插入图片描述

拷贝项目到本地即可查看文档 案例文件 使用编辑器等

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

parcel打包工具

就像前端需要webpack来进行模块化开发 three JS也需要一个对应的工具,他就是PARCEL https://www.parceljs.cn/

当然,也可以使用前端常用的webpack vite等 各有优劣

parcel的优势在于上手速度快 0配置

在这里插入图片描述

本地新建空文件夹 使用npm init 命令创建新的NPM包

npm install -g parcel-bundler 安装parcel

手动建立如图的目录结构与基础文件 参考

在这里插入图片描述

在这里插入图片描述

到此 使用parcel搭建的最基础的threeJS开发环境就完成了

可以拿到ThreeJS的各种接口 方法 常量等

在这里插入图片描述

搭建第一个场景和物体

打开上文提到的本地编辑器

。。。感觉和当年在学校学的unity很像,如果有过类似经验,理解上手会很快

相机,即使观察视角

场景,即被观察的物体

在这里插入图片描述

理解相机的视锥体概念,官方案例

const camera = new THREE.PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )

fov — 摄像机视锥体垂直视野角度

aspect — 摄像机视锥体长宽比

near — 摄像机视锥体近端面

far — 摄像机视锥体远端面

这些参数一起定义了摄像机的viewing frustum(视锥体)。

在这里插入图片描述

main.js案例代码

// 1.创建场景const scene = new THREE.Scene() // 2.创建并添加相机const camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000) camera.position.set(0,0,10) //设置相机位置scene.add(camera)// 3. 在场景中创建并添加物体const cubGeometry = new THREE.BoxGeometry( 1, 1, 1 );// 创建几何立方体const cubMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00} );// 添加材质const cube = new THREE.Mesh( cubGeometry, cubMaterial );// 将几何体和材质组合为物体scene.add( cube );// 4.设置渲染器并渲染场景const renderer = new THREE.WebGL1Renderer() //初始化渲染器renderer.setSize(window.innerWidth,window.innerHeight) //设置渲染的尺寸大小document.body.appendChild(renderer.domElement);// 将webgl渲染的内容添加到bodyrenderer.render(scene,camera) // 使用渲染器,通过相机将场景渲染进来

案例成功

在浏览器中会渲染出一个固定视角下观察到的,黑色场景内的绿色绿色正方体的一面。

在这里插入图片描述

轨道控制器查看物体

// 引入轨道控制器import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';// 5.添加控制器-轨道控制器 可以使得相机围绕目标进行轨道运动 通过不断地重新渲染来实现移动视角的效果const controls = new OrbitControls(camera,renderer.domElement);function render(){ //递归实现每一帧的重新渲染 renderer.render(scene,camera) requestAnimationFrame(render);}render();

在这里插入图片描述

添加坐标轴辅助器

const axesHelper = new THREE.AxesHelper( 5 );scene.add( axesHelper );

在这里插入图片描述

设置物体移动

function render(){ //递归实现每一帧的重新渲染 // 7.通过循环 不断修改物体的位置 cube.position.x += 0.1; if(cube.position.x > 5){ cube.position.x = 0; } renderer.render(scene,camera) requestAnimationFrame(render);}

在这里插入图片描述

设置物体缩放和旋转

// 物体的缩放cube.scale.set(3,2,1);//指物体在X,Y,Z轴上的缩放倍数// 物体的旋转 ———— 欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体。cube.rotation.set(Math.PI/4,0,0,'XYZ')//物体沿X轴旋转90度

在这里插入图片描述

动画

gsap 补间动画: https://www.npmjs.com/package/gsap

文档:https://greensock.com/ 动画速度调整

// 9.gsap 补间动画 —— 无需再手动计算时间做动画了 内置API自动生成 并能通过丰富的参数和回调进行控制let animate_line = gsap.to(cube.position,{ x: 5 , //轴对象与动画值 duration: 5 , //持续时间 ease: "power1.inOut" , //速度 repeat:-1 , //重复次数 yoyo:true , //往返运动 delay:2 , //延迟动画开始时间 onComplete:()=>{ //动画完成的回调函数 }, onStart:()=>{ //动画开始的回调函数 }});let animate_rotat = gsap.to(cube.rotation,{ x: Math.PI*2 , duration: 5 , repeat:-1 , ease: "power1.inOut"});window.addEventListener("click",()=>{ //单击屏幕暂停 animate_line.isActive()?animate_line.pause():animate_line.resume() animate_rotat.isActive()?animate_rotat.pause():animate_rotat.resume()})window.addEventListener("dbclick",()=>{ //双击屏幕全屏/取消全屏 document.fullscreenElement?document.exitFullscreen():renderer.domElement.requestFullscreen()})function render(){ //递归实现每一帧的重新渲染 // 8.制作动画 —— 时间动画原理(通过每一帧的生成时间为基准来渲染动画 clock函数或render自带时间参数) 较简单 略... renderer.render(scene,camera) requestAnimationFrame(render);}render();

在这里插入图片描述

设置阻尼与监听画面变化渲染画面

// 10.设置控制器阻尼 更真实 需要在每一帧重新生成时调用controls.update();controls.enableDamping = true;// 11.监听画面变化渲染画面 window.addEventListener('resize',()=>{ camera.aspect = window.innerWidth/window.innerHeight//更新相机 camera.updateProjectionMatrix();//更新相机投影矩阵 renderer.setSize(window.innerWidth,window.innerHeight)//更新渲染器尺寸大小 renderer.setPixelRatio(window.devicePixelRatio)//设置渲染器像素比————就是缩放比})

在这里插入图片描述

基本案例完成

总结主要步骤:

创建场景创建并添加相机在场景中创建并添加物体(几何体+材质)添加轨道控制器与阻尼、 补间动画(移动 缩放 旋转等)、监听画面变化重新渲染、辅助坐标轴等设置渲染器并渲染场景

入门部分的进阶内容

GUI库 —轻量级UI界面控制库

安装npm install dat.gui,

很方便的工具 感觉也可以用在echarts上

import * as dat from 'dat.gui';// 12. GUI 实现开发中快速调试物体的效果const gui = new dat.GUI();// 折叠目录 设置文件夹let folder = gui.addFolder('设置立方体')//材质工具栏folder.add(cube.material,'wireframe').name('材质');//坐标轴工具栏folder.add(cube.position,'x').min(0).max(5).step(0.001).name('移动X轴坐标').onChange((val)=>{ // 回调函数 val是修改后的值}).onFinishChange((val)=>{ // 回调函数 val是修改后的值});//展示工具栏folder.add(cube,'visible').name('是否展示')const params = { color:'#ffff00', fn:()=>{ //动画 gsap.to(cube.position,{ x: 5 , //轴对象与动画值 duration: 3 , //持续时间 ease: "power1.inOut" , //速度 repeat:-1 , //重复次数 yoyo:true , //往返运动 delay:2 , //延迟动画开始时间 onComplete:()=>{ //动画完成的回调函数 }, onStart:()=>{ //动画开始的回调函数 } }); }};//颜色工具栏folder.addColor(params,'color').onChange((val)=>{ // 回调函数 val是修改后的值 cube.material.color.set(val);})//动画工具栏 点击触发特定效果folder.add(params,'fn').name('移动动画')

在这里插入图片描述

几何体

基础概念:

正方体坐标:最基础的正方体拥有六个面,每个面4个点,共24个点。每个点都需要xyz三轴的坐标,即72个坐标数据。

法相量:面的朝向角度

在这里插入图片描述

// 13. 通过顶点设置创建几何体矩形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));const Material = new THREE.MeshBasicMaterial({ color: 0x00ff00} );// 添加材质const cube = new THREE.Mesh( geometry, Material );// 将几何体和材质组合为物体

在这里插入图片描述

常用网格几何体:

立方缓冲几何体、圆形缓冲几何体、圆锥缓冲几何体、圆柱缓冲几何体、八面缓冲几何体、十二面缓冲几何体、二十面缓冲几何体、边缘缓冲几何体、挤压缓冲几何体、车削缓冲几何体、参数化缓冲几何体、平面缓冲几何体、多面缓冲几何体、圆环缓冲几何体、形状缓冲几何体、球缓冲几何体、圆环缓冲扭结几何体、管道缓冲几何体、网格几何体

纹理与材质

物体=几何体+纹理与材质

相同的形状,加上不同的纹理即可表示不同的物体。例如长方体,可能是板砖,可能是烟盒,也可能是手机等

纹理

// 14 纹理与材质// 导入纹理 通过图片加载const textureLoader = new THREE.TextureLoader();const wenliColorTexture = textureLoader.load('./textures/123.png')// const wenliColorTexture = textureLoader.load(require('../assets/imgs/textures/123.pn'))// 添加物体const cubeGeometry = new THREE.BoxBufferGeometry(1,1,1)//材质const BasicMaterial = new THREE.MeshBasicMaterial({ color:'#ffff00', map:wenliColorTexture,})const cube = new THREE.Mesh( cubeGeometry, BasicMaterial );// 将几何体和材质组合为物体scene.add( cube );//在场景中添加物体

在这里插入图片描述

纹理偏移 旋转 重复等常用属性

// 14 纹理与材质// 导入纹理 通过图片加载const textureLoader = new THREE.TextureLoader();const wenliColorTexture = textureLoader.load(require('../assets/imgs/textures/123.png'));//纹理加载器// 纹理常用属性wenliColorTexture.offset.set(0.5,0.5);//偏移wenliColorTexture.rotation = Math.PI/4;///旋转45°wenliColorTexture.center.set(0.5,0.5);///旋转原点 默认(0,0)wenliColorTexture.repeat.set(2,3);///重复次数wenliColorTexture.wrapS = THREE.MirroredRepeatWrapping;///重复模式 水平方向 镜像重复wenliColorTexture.wrapT = THREE.RepeatWrapping;///重复模式 垂直方向 无限重复

在这里插入图片描述

纹理显示算法与mipmap

// 纹理的显示算法// wenliColorTexture.minFilter = THREE.NearestFilter;//一纹素小于一像素时,贴图如何采样// wenliColorTexture.magFilter = THREE.NearestFilter;//一纹素大于一像素时,贴图如何采样wenliColorTexture.minFilter = THREE.LinearFilter;//默认值wenliColorTexture.magFilter = THREE.LinearFilter;//默认值

在这里插入图片描述

在这里插入图片描述

透明材质与透明纹理

类似铁栅栏场景 需要部分透明 遵循黑遮白显的原则

// 纹理的透明const wenliAplhaTexture = textureLoader.load(require('../assets/imgs/alphaMap/alphaMap.png'));//纹理加载器加载不透明度灰阶图片// 添加物体const cubeGeometry = new THREE.BoxBufferGeometry(1,1,1)//材质const BasicMaterial = new THREE.MeshBasicMaterial({ color:'#ffff00', map:wenliColorTexture, alphaMap:wenliAplhaTexture,//控制表面透明度(黑色完全透明 白色完全不透明 灰阶代表不透明度) transparent:true,//允许透明})const cube = new THREE.Mesh( cubeGeometry, BasicMaterial );// 将几何体和材质组合为物体scene.add( cube );//在场景中添加物体

纹理图片

在这里插入图片描述

设置透明纹理图片

在这里插入图片描述

(用PS调整了半天 一开始图片没选好效果不尽如人意了23333)

在这里插入图片描述

PBR——材质

基于物理渲染,通过计算环境光线使物体更真实

灯光属性(Light Properties): 直接照明:直接从光源发射的光至物体表面,造成直接漫反射(整体亮)或高光(局部亮)间接照明: 环境光直接光等二次反射至物体表面,造成间接漫反射或高光直接高光间接高光阴影环境光闭塞 表面属性(Surface Properties) 基础色法线:使得表面显得凹凸不平,用于添加表面细节,是一种三维向量(Vector3)数据高光:定义物体表面不同区域上不同的反射率来区分不同的物体材质粗糙度金属度

标准网格材质与光照效果

漫反射环境光

// 添加物体const cubeGeometry = new THREE.BoxBufferGeometry(1,1,1)// 14 纹理与材质 纹理常用属性 显示算法 纹理材质的透明// 导入纹理 通过图片加载const textureLoader = new THREE.TextureLoader();const wenliColorTexture = textureLoader.load(require('../assets/imgs/textures/123.png'));//纹理加载器// const BasicMaterial = new THREE.MeshBasicMaterial({//基础材质 MeshBasicMaterial// 15. 标准物理网格材质 MeshStandardMaterial 🔺需要灯光和表面属性const material = new THREE.MeshStandardMaterial({ map:wenliColorTexture, color:'#62b5eb',})const cube = new THREE.Mesh( cubeGeometry, material );// 将几何体和材质组合为物体scene.add( cube );//在场景中添加物体// 16. 设置灯光// 环境光(颜色 强度)const light = new THREE.AmbientLight(0XFFFFFF,0.8);// 直线平行光(颜色 强度 位置)const directionLight = new THREE.DirectionalLight(0XFFFFFF,0.8);directionLight.position.set(10,10,10)scene.add( light );

在这里插入图片描述

漫反射平行光

// 16. 设置灯光// 环境光(颜色 强度)const light = new THREE.AmbientLight(0XFFFFFF,0.8);// 直线平行光(颜色 强度 位置)const directionLight = new THREE.DirectionalLight(0XFFFFFF,0.8);directionLight.position.set(10,10,10)scene.add( directionLight);

在这里插入图片描述

粗糙度与粗糙贴图,金属度与金属贴图,法线贴图

粗糙度

const material = new THREE.MeshStandardMaterial({ map:wenliColorTexture, color:'#62b5eb', roughness:0,//15.2 粗糙度 0-1 数值越小越光滑 // roughnessMap:0,//粗糙度贴图 暂无资源})

在这里插入图片描述

金属度

const material = new THREE.MeshStandardMaterial({ map:wenliColorTexture, color:'#62b5eb', roughness:0,//15.2 粗糙度 0-1 数值越小越光滑 // roughnessMap:0,//粗糙度贴图 暂无资源 metalness:1,//15.3 金属度 0-1 数值越小越不像金属 // metalnessMap:0,//金属度贴图 暂无资源})

在这里插入图片描述

法线:使得表面显得凹凸不平,用于添加表面细节,通过设置对应贴图后效果是不同区域内面对光的反射效果不同,使物体看起来更真实。无图片资源,下面仅作门的案例展示

设置法线贴图前

在这里插入图片描述

法线贴图

在这里插入图片描述

设置法线贴图后

在这里插入图片描述

纹理材质资源网站

到这里已经不难发现,再ThreeJS开发中,各种图片资源对于最终的效果呈现有着相当重要的作用。

这里有推介的资源网站:

https://www.poliigon.com/texture ,部分收费

在这里插入图片描述

https://3dtextures.me/

http://arroway-textures.ch/textures/,注册EPIC 虚幻引擎账号可以免费试用资源

纹理加载进度-设置加载管理器 LoadingManager

LoadingManager 跟踪已加载和待加载的数据

// 16 设置加载管理器LoadingManager 跟踪已加载和待加载的数据let event = { }event.onLoad = function(){ console.log('所有资源图片加载完成')}event.onProgress = function(url,num,total){ console.log(url+'加载完成') console.log('加载进度:'+num) console.log('资源总数:'+total) console.log('加载进度百分比:'+(num/total*100).toFixed(2)+'%')}event.onError = function(e){ console.log(e) console.log('图片加载错误')}const loadingManager = new THREE.LoadingManager( event.onLoad,event.onProgress,event.onError);// 14 纹理与材质 纹理常用属性 显示算法 纹理材质的透明// 导入纹理 通过图片加载const textureLoader = new THREE.TextureLoader(loadingManager);

在这里插入图片描述

环境纹理贴图 物体环境贴图与场景环境贴图

像镜子一样 将周围场景的图像映射在物体上

需要设置三个轴两个方向上的图片属性,共六张图片。

// 17. 环境贴图const cubeTextureLoader = new THREE.CubeTextureLoader();const envMapTetxture = cubeTextureLoader.load([ require('../assets/imgs/textures/envMapTetxture/px.jpg'), require('../assets/imgs/textures/envMapTetxture/nx.jpg'), require('../assets/imgs/textures/envMapTetxture/py.jpg'), require('../assets/imgs/textures/envMapTetxture/ny.jpg'), require('../assets/imgs/textures/envMapTetxture/pz.jpg'), require('../assets/imgs/textures/envMapTetxture/nz.jpg'),])const spherGeometry = new THREE.SphereBufferGeometry(1,20,20);const material = new THREE.MeshStandardMaterial({ metalness:0.7,//15.3 金属度 0-1 数值越小越不像金属 roughness:0.1,//15.2 粗糙度 0-1 数值越小越光滑 envMap:envMapTetxture,//17.1 物体环境贴图});//17.2 场景环境贴图 缺少资源 待续。。。const sphere = new THREE.Mesh( spherGeometry, material );// 将几何体和材质组合为物体scene.add( sphere );//在场景中添加物体

在这里插入图片描述

环境纹理贴图 物体环境贴图与场景环境贴图

//17.2 给场景添加背景scene.background = envMapTetxture;//17.3 给场景所有物体添加默认的环境贴图scene.environment = envMapTetxture;

在这里插入图片描述

灯光属性

待续。。。

纹理材质初步学习完成

总结:

创建场景创建并添加相机 、可设置轨道控制器与阻尼在场景中创建并添加物体、可设置动画效果(移动 缩放 旋转等) 几何体 多个三维坐标点组成一个面或二维图形,多个面组成一个几何体。

手动标记点和面生成或利用已有API生成。纹理:通过纹理API设置纹理的颜色、各种效果贴图、偏移、旋转、重复、透明贴图、环境遮挡等。并设置纹理显示算法材质 :通过物理渲染使物体更真实。设置灯光属性的照明与反射效果,阴影等;设置物体表面的基础色、法线贴图、高光、粗糙度及粗糙度贴图、金属度及金属度贴图等。 设置渲染器并渲染场景、监听画面变化重新渲染、辅助坐标轴等GUI轻量级UI界面控制库



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。