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

Posted 跳动的世界线

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端前沿web 3d可视化技术 ThreeJS学习全记录相关的知识,希望对你有一定的参考价值。

前端前沿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渲染的内容添加到body
renderer.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)

环境遮挡贴图与强度

待续

Webgl/Threejs 学习及实践总结

  1. Webgl 是什么?前端使用场景有哪些?


  • webgl (web图形库),是根据k OpenGL ES  2.0来实现的一套 JavaScriptAPI,可以在浏览器中渲染交互式3D和2D图形。该API可以在HTML5 canvas 元素中使用,可以利用用户设备的 GPU 加速。


  • 前端可以使用 threejs 库进行开发, threejs 是基于原生webgl 封装运行的三维引擎,将3D渲染中重要的工具方法与渲染循环封装起来,简化细节。前端可以在客户端创建3d场景、模型等,业务具体场景:3D可视化、产品720度预览(车、房)、海量数据可视化、h5/微信游戏、webVR。


  • 使用Detector.js检测:判断canvas兼容、webgl兼容。


 
   
   
 
  • 兼容性:

  • 移动端:Android 5以上、iOS 8以上。


     2. Threejs 基础知识点


2.1 基本概念


场景(scene) :容纳一切的容器


相机(camera) :在webGL世界里面的眼睛。


视口(viewport) :3D场景渲染的二维图像,从浏览器的canvas元素上看到的场景。


视野(field of view) :相机可见范围左右边界的夹角。


视锥体(view frustum) :物体可以被渲染到视口的空间(只有处于视锥体空间内部的物体,才可以被看见)


近裁剪面(near clipping plane) :视锥体靠近相机的一面,其实就是视口。


远裁剪面(far clipping plane) :视锥体最远离相机的平面。


2.2 公式部分


弧长公式:l = n Math.PI r / 180  (n 圆心角)


角度转换公式:30 * Math.PI/180  // 30度


2.3 方法


  • vector1.angleTo(vector2)  // 两个向量间弧长


  • camera.getWorldDirection()  // camera角度


  • 设置垂直于自身位置向量:


// 围绕自身向量旋转
var fundVec = new THREE.Vector3(0,0,1);
var selfVec = new THREE.Vector3(x, y, z).normalize();
var newVec = new THREE.Quaternion().setFromUnitVectors(fundVec, selfVec);
hotArea.quaternion.multiply(newVec);

  • 坐标转换:

// 3d 坐标转 2d
var qiutweenPos = qiu163.getWorldPosition();
var vec = new THREE.Vector3();
var widthHalf = window.innerWidth / 2;
var heightHalf = window.innerHeight / 2;
qiu163.updateMatrixWorld();
vec.setFromMatrixPosition(qiu163.matrixWorld);
vec.project(camera);
vec.x = ( vec.x widthHalf ) + widthHalf;
vec.y = - ( vec.y 
heightHalf ) + heightHalf;
console.log(vec)


  • 横竖屏事件监听方法:

//横竖屏事件监听方法
var innerWidthTmp = window.innerWidth;
function screenOrientationListener() {
 try {
   var iw = window.innerWidth;
   var orientation;
   //屏幕方向改变处理
   if (iw != innerWidthTmp) {
     if (iw > window.innerHeight) orientation = 90;
     else orientation = 0;
     //调用转屏事件
     onWindowResize();
     innerWidthTmp = iw;
   }
 } catch (e) {
   console.log(e);
 };
 //间隔固定事件检查是否转屏,默认300毫秒
 setTimeout(screenOrientationListener, 300);
}
//启动横竖屏事件监听
screenOrientationListener();

      3. Threejs + Tweenjs 

可实现 3d 场景中动画效果


       4. 坑(tip)


  • dpr > 2 的机型如:iphone6plus 等,需注意模型太大时,会导致闪退,需要设置:

  • renderer.setPixelRatio(window.devicePixelRatio<3 ? window.devicePixelRatio : 1)

  • multi polygon 贴图 可置换成 spotlight 打灯上色

  • 1个geometry对应1个材质 在c4d中

  • c4d 中的坐标position 3js要使用 getWorldPosition()

  • geometry为sphere 贴图默认是sphere投射贴图,

  • geometry为uv 贴图默认是uvw投射贴图,多面体mesh需注意

  • 选择模型loader需要注意,如果场景复杂建议直接用dae格式文件,可导出camera light 播放动画等,不足之处在于需要自行添加材质material。

  • 如果使用单个模型可用 obj格式,材质也可以导出为mtl格式。



以上是关于前端前沿web 3d可视化技术 ThreeJS学习全记录的主要内容,如果未能解决你的问题,请参考以下文章

gis和threejs的学习资料

Web3D框架——选threejs还是thingjs?

基于Threejs的web 3D开发入门

Webgl/Threejs 学习及实践总结

Web 3D智慧变电站三维工程进度系统-WebGL/Threejs实战开发

学习Threejs的那些事儿