• three.js学习笔记


    开始

    three.js官网地址:Three.js – JavaScript 3D Library

    因为官网是部署在国外访问的时候比较卡

    我们可以把项目下载下来在本地运行

    第一步打开官网找到github选项

     第二步克隆一份

     下载到本地npm i 下载对应依赖启动就可以了

    第三步启动项目进入这样的页面点击docs

     进入之后你可以选择语言之后再看文档的时候就不用等待那么长时间了 

    如果你有VPN可以省略以上步骤

     npm install three --save      //下载three

    创建一个场景(Creating a scene)

    这一部分将对three.js来做一个简要的介绍;我们将开始搭建一个场景,其中包含一个正在旋转的立方体。看代码

    1. import * as THREE from 'three'
    2. // console.log(THREE)
    3. // 创建一个场景
    4. const scene = new THREE.Scene();
    5. // 创建一个透视相机
    6. const camera = new THREE.PerspectiveCamera(
    7. 75,
    8. window.innerWidth / window.innerHeight,
    9. 0.1,
    10. 1000)
    11. // 设置相机位置
    12. camera.position.set(0,0,10)
    13. scene.add(camera)
    14. // 添加物体
    15. // 创建集合体
    16. const cubeGeometry=new THREE.BoxGeometry(1,1,1);
    17. const cubeMaterial=new THREE.MeshBasicMaterial({color:'red'});
    18. // 根据几何体和材质创建物体
    19. const cube=new THREE.Mesh(cubeGeometry,cubeMaterial)
    20. // 将集几何体添加到场景中
    21. scene.add(cube)
    22. // 初始化渲染器
    23. const renderer=new THREE.WebGL1Renderer()
    24. // 设置渲染的尺寸大小
    25. renderer.setSize(window.innerWidth, window.innerHeight);
    26. // 将webGl渲染的canvas内容添加到body
    27. document.body.appendChild(renderer.domElement);
    28. // 使用渲染器,通过相机将场景渲染出来
    29. renderer.render(scene,camera)

    three.js里有几种不同的相机,在这里,我们使用的是PerspectiveCamera(透视摄像机)。

    第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)。

    第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。比如说,当你在一个宽屏电视上播放老电影时,可以看到图像仿佛是被压扁的。

    接下来的两个参数是近截面(near)和远截面(far)。 当物体某些部分比摄像机的远截面远或者比近截面近的时候,该这些部分将不会被渲染到场景中。或许现在你不用担心这个值的影响,但未来为了获得更好的渲染性能,你将可以在你的应用程序里去设置它。

    接下来是渲染器。这里是施展魔法的地方。除了我们在这里用到的WebGLRenderer渲染器之外,Three.js同时提供了其他几种渲染器,当用户所使用的浏览器过于老旧,或者由于其他原因不支持WebGL时,可以使用这几种渲染器进行降级。

    除了创建一个渲染器的实例之外,我们还需要在我们的应用程序里设置一个渲染器的尺寸。比如说,我们可以使用所需要的渲染区域的宽高,来让渲染器渲染出的场景填充满我们的应用程序。因此,我们可以将渲染器宽高设置为浏览器窗口宽高。对于性能比较敏感的应用程序来说,你可以使用setSize传入一个较小的值,例如window.innerWidth/2window.innerHeight/2,这将使得应用程序在渲染时,以一半的长宽尺寸渲染场景。

    如果你希望保持你的应用程序的尺寸,但是以较低的分辨率来渲染,你可以在调用setSize时,将updateStyle(第三个参数)设为false。例如,假设你的 标签现在已经具有了100%的宽和高,调用setSize(window.innerWidth/2, window.innerHeight/2, false)将使得你的应用程序以一半的分辨率来进行渲染。

    最后一步很重要,我们将renderer(渲染器)的dom元素(renderer.domElement)添加到我们的HTML文档中。这就是渲染器用来显示场景给我们看的元素。

    到这里我们可以看到图形是个二维的图形,那如何显示三位的图形那,如下

    渲染场景

    现在,如果将之前写好的代码复制到HTML文件中,你不会在页面中看到任何东西。这是因为我们还没有对它进行真正的渲染。为此,我们需要使用一个被叫做“渲染循环”(render loop)或者“动画循环

    1. function render() {
    2. // 渲染下一帧的时候就会调用render函数
    3. requestAnimationFrame(render);
    4. renderer.render(scene, camera);
    5. }
    6. render()

    效果如下

    效果

    可以看到效果图中的辅助线并且让图形自己转动加上以下代码即可

    1. //++++++++++
    2. const axesHelper = new THREE.AxesHelper( 5 );
    3. scene.add( axesHelper );
    4. //++++++++++
    5. function render() {
    6. // 渲染下一帧的时候就会调用render函数
    7. requestAnimationFrame(render);
    8. //++++++++
    9. cube.rotation.x += 0.01;
    10. cube.rotation.y += 0.01;
    11. //++++++++
    12. renderer.render(scene, camera);
    13. }
    14. render()

    那我们要沿着轴线移动可以设置物体的position属性如下

    1. // 根据几何体和材质创建物体
    2. const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
    3. //+++++++++
    4. // 修改物体的位置
    5. cube.position.set(0,0,5)
    6. //我们也可以position.x直接去修改物体所在轴的位置比如cube.position.x=5;
    7. //+++++++++
    8. // 将集几何体添加到场景中
    9. scene.add(cube)

     如果要让物体自己移动直接在渲染循环做一个判断就可以了

    1. function render() {
    2. // 渲染下一帧的时候就会调用render函数
    3. requestAnimationFrame(render);
    4. //++++++++++++
    5. cube.position.z += 0.01
    6. if (cube.position.z>5) {
    7. cube.position.z=0
    8. }else if(cube.position==0){
    9. cube.position.z += 0.01
    10. }
    11. //++++++++++++
    12. renderer.render(scene, camera);
    13. }
    14. render()

    物体的缩放和旋转

    1. // 物体的缩放
    2. cube.scale.set(3,2,1)
    1. // 物体的旋转 前三个参数是xyz第四个参数可以是字符串比如'ZXY'来设置旋转顺序注意字母要大写
    2. cube.rotation.set(Math.PI/4,0,0)

    应用requestAnimationFrame

    render()函数有默认参数time我们打印看看

     我们看出当我们渲染的时候这个频率有的差距会比较高,我们可以让他没一段时间让物体移动多少,如下;

    1. //这里的%5就是对步长取余,每次进入渲染循环就对时间经行取余,比如1%5=1,一直到5%5=0来实现以上的if判断进行的循环
    2. let t = (time / 1000) % 5;
    3. cube.position.x = t * 1;

    这样的计算方式太复杂我们可以使用psap动画库

    psap动画库基本使用原理

    gsap - npm 官网

    下载并引入

    npm install gsap
    1. // 导入gsap
    2. import gsap from 'gsap';
    1. //控制cube的位置属性position x让x移动到5的位置 duration在5秒内
    2. gsap.to(cube.position,{x:5,duration:5})
    3. //控制cube的位置旋转rotation x让x旋转360度 duration在5秒内
    4. gsap.to(cube.rotation,{x:2*Math.PI,duration:5})
    5. function render(time) {
    6. // 渲染下一帧的时候就会调用render函数
    7. renderer.render(scene, camera);
    8. requestAnimationFrame(render);
    9. }

    接下来我看如何设置速度

    进入之后可以看到一下的效果 有不同的实现线的实现

     

     我们还可以设置动画完成和结束的回调函数 

    1. var animate1 =gsap.to(cube.position,{
    2. x:5,
    3. // 重复的次数 无线循环为-1
    4. repeat:2,
    5. // 往返的运动
    6. yoyo:true,
    7. // 延迟2秒移动
    8. delay:2,
    9. duration:5,ease:'power1.inOUt',
    10. onComplete:()=>{
    11. console.log('动画完成');
    12. },
    13. onStart:()=>{
    14. console.log('动画开始');
    15. }
    16. })
    17. // 监听事件 dbclick监听鼠标双击
    18. window.addEventListener('dblclick',()=>{
    19. // 判断是否是运动状态
    20. if(animate1.isActive()){
    21. // 如果是 停止
    22. animate1.pause();
    23. }else{
    24. // 否 运动
    25. animate1.resume()
    26. }
    27. })

    设置阻尼 拖动物体结束时产生惯性

    1. // 将其设置为true以启用阻尼(惯性),这将给控制器带来重量感。默认值为false。
    2. // 请注意,如果该值被启用,你将必须在你的动画循环里调用.update()。
    3. controls.enableDamping=true
    4. function render(time) {
    5. // 渲染下一帧的时候就会调用render函数
    6. controls.update()
    7. renderer.render(scene, camera);
    8. requestAnimationFrame(render);
    9. }

    根据尺寸变化实现自适应

    1. // 监听画面变化,更新渲染函数
    2. window.addEventListener('resize',()=>{
    3. // 更新摄像头
    4. camera.aspect=window.innerWidth/window.innerHeight;
    5. // 更新摄像机的投影矩阵
    6. camera.updateProjectionMatrix();
    7. // 更新渲染器
    8. renderer.setSize(window.innerWidth,window.innerHeight);
    9. // 设置渲染器的像素比
    10. renderer.setPixelRatio(window.devicePixelRatio);
    11. })

    调用js接口控制画布全屏和退出全屏

    1. // 调用js接口控制画布全屏和退出全屏
    2. window.addEventListener('dblclick',()=>{
    3. const fullScreenElement=document.fullscreenElement;
    4. if (!fullScreenElement){
    5. // 双击控制屏幕进入全屏,退出全屏
    6. // 让画布全屏
    7. renderer.domElement.requestFullscreen();
    8. }else{
    9. // 退出全屏 使用document对象
    10. document.exitFullscreen();
    11. }
    12. })

    应用图形用户界面更改变量

    我们需要下载gui库👉官网 dat.gui - npm 

     npm install --save dat.gui
    1. // 导入data.gui
    2. import * as dat from 'dat.gui'
    1. const gui=new dat.GUI();
    2. // min 最小值 max最大值 step每移动多少距离 name名字 onChange值被修改时触发该事件 onFinishChange修改完成时触发的事件
    3. gui.add(cube.position,'x').min(0).max(5).step(0.01).name('移动x轴').onChange((value)=>{console.log('修改时触发的函数');}).onFinishChange((value)=>{console.log('修改完成时触发的事件');})
    1. // 修改物体的颜色
    2. const params={
    3. color:'#ffff00',
    4. fn:()=>{
    5. gsap.to(cube.position,{x:5,duration:2,yoyo:true,repeat:-1})
    6. }
    7. }
    8. gui.addColor(params,'color').onChange((value)=>{
    9. console.log('值被修改',value);
    10. cube.material.color.set(value)
    11. })
    12. // 设置选项框
    13. gui.add(cube,'visible').name('是否显示')
    14. // 设置按钮点击触发某个事件
    15. gui.add(params,'fn').name('立方运动');
    16. // 功能太多的话 可以加个文件夹如下
    17. var folder=gui.addFolder('设置立方体');
    18. //显示物体的结构线
    19. folder.add(cube.material,'wireframe')

    使用BufferGeometry创建一个面片

    1. // 创建多个三角形小案例
    2. const geometry=new THREE.BufferGeometry();
    3. // 创建一个一维数组每三个点都是(x,y,z)
    4. // 立方体的每个面实际上是由2个三角形组成的。所以我们必须单独映射每个三角形
    5. const vertices=new Float32Array([
    6. -1.0,-1.0,1.0,
    7. 1.0,-1.0,1.0,
    8. 1.0,1.0,1.0,
    9. 1.0,1.0,1.0,
    10. -1.0,1.0,1.0,
    11. -1.0,-1.0,1.0
    12. ])
    13. // 创建位置告诉他这个一个一维数组每三个值为一个坐标
    14. geometry.setAttribute('position',new THREE.BufferAttribute(vertices,3));
    15. const cubeMaterial = new THREE.MeshBasicMaterial({ color: 'red' });
    16. // 根据几何体和材质创建物体
    17. const mash=new THREE.Mesh(geometry,cubeMaterial)
    18. scene.add(mash);

    来做一个小案例

    1. // 创建多个三角形小案例
    2. for (let i=0;i<50;i++){
    3. // 每一个三角形,需要3个顶点,每个顶点需要3个值
    4. const geometry=new THREE.BufferGeometry();
    5. // 创建一个一维数组每三个点都是(x,y,z)
    6. const positionArray=new Float32Array(9)
    7. for (let j=0;j<9;j++){
    8. // 创建一个随机三角形面
    9. positionArray[j]=Math.random()*10-5;
    10. }
    11. // 创建一个随机的颜色
    12. let color=new THREE.Color(Math.random(),Math.random(),Math.random())
    13. // 创建位置告诉他这个一个一维数组每三个值为一个坐标
    14. geometry.setAttribute('position',new THREE.BufferAttribute(positionArray,3));
    15. const cubeMaterial = new THREE.MeshBasicMaterial({
    16. color: color,
    17. transparent:true,//为true可以设置材质为透明
    18. opacity:0.5});
    19. // 根据几何体和材质创建物体
    20. const mash=new THREE.Mesh(geometry,cubeMaterial)
    21. console.log(mash,'??');
    22. scene.add(mash);
    23. }

    其实官网已经提供了这这些模型

    geometry 几何体

     我们可以拿官网给的这些模型去做自己想做的东西

    设置纹理显示算法与mipmap

    1. mctextture.minFilter=THREE.NearestFilter;
    2. mctextture.magFilter=THREE.NearestFilter;

     透明纹理

    1. // 导入纹理
    2. var ylj = require('../assets/imgs/img.webp')
    3. const textureLoader=new THREE.TextureLoader()
    4. const texture= textureLoader.load(ylj)
    5. // mctextture.minFilter=THREE.NearestFilter;
    6. // 添加物体
    7. // 创建集合体
    8. const cubeGeometry = new THREE.BoxGeometry( 1, 1, 1 );
    9. const cubeMaterial = new THREE.MeshBasicMaterial({
    10. color: '#fff',
    11. map:texture,
    12. alphaMap:texture,
    13. transparent:true,
    14. // 透明度
    15. opacity:0.5,
    16. // 默认显示一面 THREE.DoubleSide属性两面都显示
    17. side:THREE.DoubleSide
    18. });
    19. // 构建完设置也可以
    20. // cubeMaterial.side=THREE.DoubleSide;
    21. //添加平面
    22. const plane=new THREE.Mesh(
    23. new THREE.PlaneGeometry(1,1),
    24. cubeMaterial
    25. )
    26. plane.position.set(3,0,0);
    27. scene.add(plane);

     环境遮挡贴图

     

    1. // 环境贴图
    2. var chartlet = require('../assets/imgs/mc.webp')
    3. const textAoTexture=textureLoader.load(
    4. chartlet
    5. )
    6. // mctextture.minFilter=THREE.NearestFilter;
    7. // 添加物体
    8. // 创建集合体
    9. const cubeGeometry = new THREE.BoxGeometry( 1, 1, 1 );
    10. const cubeMaterial = new THREE.MeshBasicMaterial({
    11. color: '#fff',
    12. map:texture,
    13. alphaMap:texture,
    14. transparent:true,
    15. //环境遮挡贴图
    16. aoMap:textAoTexture,
    17. // 阴影亮度
    18. // aoMapIntensity:0.5,
    19. // 透明度
    20. // opacity:0.5,
    21. // 默认显示一面 THREE.DoubleSide属性两面都显示
    22. side:THREE.DoubleSide
    23. });
    24. // 构建完设置也可以
    25. // cubeMaterial.side=THREE.DoubleSide;
    26. //添加平面
    27. const PlaneGeometry=new THREE.PlaneGeometry(1,1);
    28. const plane=new THREE.Mesh( PlaneGeometry,cubeMaterial);
    29. // 给平面设置二组uv  第一组uv控制的是颜色贴图,第二组uv控制的是光照效果,和虚幻4引擎一个意思
    30. PlaneGeometry.setAttribute(
    31. 'uv2',//第二组uv
    32. new THREE.WebGLRenderer(PlaneGeometry.attributes.uv.array,2) //本身的uv 2个值作为一个点
    33. )
    34. plane.position.set(3,0,0);
    35. scene.add(plane);

  • 相关阅读:
    【QT】Qt Creator生成动态库(DLL)并调用
    Bug:.tar文件解压后提示“不可信的旧时间戳”解决方案
    分布式链路追踪之Skywalking从入门到CRUD
    前端技能树,面试复习—— 模拟题+真题系列(2): 单例模式 | 大文件上传 | SSR 原理 | 收集依赖 | HOOK 原理 | Worker 等等
    (十二)Spring IoC注解式开发
    如何通过Java导出带格式的 Excel 数据到 Word 表格
    腾讯云CVM服务器标准型/高IO/计算/大数据使用场景及选择说明
    新白娘子传奇系列
    大数据必学Java基础(十八):条件运算符和位运算符
    PowerDesigner反向导入表+PowerDesigner的ER图设计+PowerDesigner连接外键的线(版本16.5)
  • 原文地址:https://blog.csdn.net/Z_Gleng/article/details/127464048