threejs学习

重要概念(场景、相机、渲染器)

如下图所示,我们最终看到浏览器上生成的内容是通过虚拟场景和虚拟相机被渲染器渲染后的结果,下面首先介绍这三个概念,将贯穿所有简单复杂的threejs项目。
在这里插入图片描述

场景 Scene

虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界

    const scene = new THREE.Scene() //创建场景

	//添加元素如模型、灯光等
	scene.add(元素)

相机 Camera

透视投影相机,如果渲染远小近大–透视投影相机,不需要远小近大–正投影相机,常用透视投影相机
正投影相机:OrthographicCamera
透视投影相机:PerspectiveCamera PerspectiveCamera( fov, aspect, near, far )

  • fov:相机视锥体竖直方向视野角度 ,默认50
  • aspect:相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height ,默认1
  • near:相机视锥体近裁截面相对相机距离,默认0.1
  • far:相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向,默认2000
    在这里插入图片描述

具体使用:

      const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000); 
      // 设置相机位置
      camera.position.set(15, 20, 50); 
      //控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标
	  camera.lookAt(0, 0, 0)//0,0,0是坐标圆点,(x,y,z)

注意: 相机实际形成一个视锥体,在这个范围内的物体才会被渲染出来,之外的物体不会被渲染到canvas上

在我们开发的时候,如果将far设置的比较小,在放大物体的时候,可以看到部分模型消失,只渲染了一部分,那么就是模型超出视锥体了,所以不被渲染。

渲染器 WebGLRenderer

有了相机和物体,则需要完成拍照,渲染器其实可以理解为拍照。
renderer=new THREE.WebGLRenderer();进行创建 .domElement可以获取到对应的元素。
renderer.render(场景, 相机);:执行渲染的操作,类似我们按下相机快门的操作。

//创建一个渲染器,一个canvas场景
const renderer = new THREE.WebGLRenderer();

renderer.setPixelRatio(window.devicePixelRatio);  //设置像素比,如果你遇到你的canvas画布输出模糊问题一定要设置

renderer.setSize(window.innerWidth, window.innerHeight); //设置canvas的大小
//锯齿属性
//renderer.antialias = true

//插入html中,这里即添加到id为container的标签下
const container = document.getElementById('container')
container.appendChild(renderer.domElement);

//最重要的!!!!渲染
renderer.render(scene, camera); //执行渲染操作

画布自适应

   window.addEventListener("resize", this.resize);
    resize() {
      //   console.log("画面变化了");
      // 更新摄像头
      let camera = this.camera
      camera.aspect = window.innerWidth / window.innerHeight;
      //   更新摄像机的投影矩阵,如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      camera.updateProjectionMatrix();
      let renderer = this.renderer
      //   更新渲染器
      renderer.setSize(window.innerWidth, window.innerHeight);
  
      //   设置渲染器的像素比
      renderer.setPixelRatio(window.devicePixelRatio);
    },

光源和材质

首先介绍几个相关的概念:

  1. 外观-材质Material:想定义物体的外观效果,比如颜色,就需要通过材质Material相关的API实现。
    在这里插入图片描述
  2. 物体-网格模型Mesh:在threejs中可以通过网格模型Mesh (opens new window)表示一个虚拟的物体,比如一个箱子、一个鼠标。
  3. 模型位置position
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);

光源

光源Light:影响物体的明暗效果
不同材质对光照的影响不同,如图(材质不止下图,可到官网查看)
在这里插入图片描述
光源也有很多,具体的API推荐看官网(太多啦!)
在这里插入图片描述

材质

基础介绍,具体内容看官网
在这里插入图片描述

几何体

Three.js提供了各种各样的几何体API,用来表示三维物体的几何形状。
因为太多了就不在此赘述,可以到官网去查看对应的api和属性

在这里插入图片描述

引入外部三维模型

引入glb\gltf模型

我们在实际工作中,总是需要另外的引入模型,这里介绍引入模型的方法

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; //加载gltf模型的加载器
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'  //解压用,不一定需要使用它

   const dracoLoader = new DRACOLoader()
   //设置解压工具位置
   dracoLoader.setDecoderPath('resources/draco/') //从node_modules\three\examples\jsm\libs\draco 中复制到public下resources/draco/  注意要从自己的项目node_modules中复制,版本不对可能会报错
   dracoLoader.setDecoderConfig({ type: "js" });
   
  const loader = new GLTFLoader()
  loader.setDRACOLoader(dracoLoader)
//在vue中默认路径是public下的
  loader.load("model/city-v1.glb", function (gltf) {  //
        const model = gltf.scene //拿到模型
        model.name = 'city' 
        model.position.set(0, 0, 0) //设置模型的位置
        model.scale.set(0.7, 0.7, 0.7)
        scene.add(model)//添加到场景中
      })

绘制天空的效果

    const loaderbox = new THREE.CubeTextureLoader() //加载CubeTexture的一个类。 内部使用ImageLoader来加载文件
      let path = 'night/', format = ".jpg"
      const cubeTexture = loaderbox.load([  //一定是六张图!!!
        path + 'posx' + format,
        path + 'negx' + format,
        path + 'posy' + format,
        path + 'negy' + format,
        path + 'posz' + format,
        path + 'negz' + format
      ])

      scene.background = cubeTexture

更改引入模型的材质

        //traverse  threejs中深度遍历的方法,会遍历所有的元素
  model.traverse(child => {
   child.material = new THREE.MeshPhongMaterial({   //只能修改mesh的外观
       color: new THREE.Color('#123ca8'),
       transparent: true,
       opacity: 0.5,
       emissiveMap: Mesh.material.map,
    })
 })

//或者,如果没有跟上一级有关系的,推荐用第一种
   model.children.forEach(item => {
      if (item.name !== selectTagName) {
       item.children.forEach(mesh => {
                mesh.material = new THREE.MeshPhongMaterial({
                  color: new THREE.Color('#123ca8'),
                  transparent: true,
                  opacity: 0.5,
                  emissiveMap: mesh.material.map,
                })
              })
		}   
	})

给模型添加点击事件

document.addEventListener('click', this.handleClick, false);

handleClick(e){
     // 射线交叉计算拾取模型
      const raycaster = new THREE.Raycaster()
      const mouse = new THREE.Vector2()
      let tag = this.tagBox
      mouse.x = (e.offsetX / this.renderer.domElement.clientWidth) * 2 - 1
      mouse.y = -(e.offsetY / this.renderer.domElement.clientHeight) * 2 + 1
      raycaster.setFromCamera(mouse, this.camera); //一定要写,不然获取的就是空

      // 获取点击到的模型的数组,从近到远排列
      const intersects = raycaster.intersectObjects(this.scene.children, true); 
       if (intersects.length > 0) {
       //intersects为点击的模型数组,可以在这里写后续的逻辑
       
       }
}

添加文字标签

可以有多种方式,这里介绍css2drender渲染
在这里插入图片描述
在animate中记得要写labelRenderer.render(scene, camera)

import {
  CSS2DObject, CSS2DRenderer
} from "three/examples/jsm/renderers/CSS2DRenderer";

  // 创建一个CSS2渲染器CSS2DRenderer
      var labelRenderer = new CSS2DRenderer();
      labelRenderer.setSize(window.innerWidth, window.innerHeight);
      labelRenderer.domElement.style.position = 'absolute';
      // 相对标签原位置位置偏移大小
      labelRenderer.domElement.style.top = '0px';
      labelRenderer.domElement.style.left = '0px';
      // //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
      labelRenderer.domElement.style.pointerEvents = 'none';
      document.body.appendChild(labelRenderer.domElement);
  

   function animate() {
        // 通过相机 场景 将结果渲染出来
        let delta = clock.getDelta();
        controls.update(delta);
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
        labelRenderer.render(scene, camera); //渲染HTML标签对象
      }
      
	//如果有写resize记得在resize中也要写上
	//labelRenderer.setSize(window.innerWidth, window.innerHeight)
	
      animate()


   // 创建标签
  function tag(name) {
      var div = document.createElement('div');
      div.innerHTML = name;
      div.classList.add('lable-text');
      //div元素包装为CSS2模型对象CSS2DObject
      var label = new CSS2DObject(div);
      div.style.pointerEvents = 'none';//避免HTML标签遮挡三维场景的鼠标事件
      gsap.to(label.position, {
        y: 2,
        repeat: -1,
        duration: 2,
        yoyo: true,
        ease: "Bounce.inOut",
      })

      return label;//返回CSS2模型标签
    }

//在想要创建标签的地方
 var label = tag('办公楼');//把粮仓名称obj.name作为标签
 var pos = new THREE.Vector3();
 model.getWorldPosition(pos);//获取obj世界坐标
 label.position.copy(pos);//位置设置为pos
 model.add(label); //添加到某模型中

ThreeJs内置工具

AxesHelper

辅助坐标系,THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。
默认y轴向上,x向右,z轴正对我们
红色:x轴
绿色:y轴
蓝色:z轴

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

DirectionalLightHelper

辅助查看DirectionalLight光源的位置

 const dirHelper = new THREE.DirectionalLightHelper(dirLight, 5);  //辅助查看光源在哪里
 scene.add(dirHelper);

如下:
在这里插入图片描述

CameraHelper

投影相机,用于模拟相机视锥体的辅助对象.它使用 LineSegments 来模拟相机视锥体.

 //方向光,常常用来表现太阳光照的效果。(颜色,强度)
   const dirLight = new THREE.DirectionalLight("rgb(253,253,253)", 10);
   dirLight.position.set(200, 200, 10);
   dirLight.castShadow = true;
   const cam = dirLight.shadow.camera;
   const cameraHelper = new THREE.CameraHelper(cam);
   scene.add(cameraHelper);
   cameraHelper.visible = true;
   scene.add(dirLight);

在这里插入图片描述
遇到问题描述:
在设置阴影时,只显示部分模型的阴影?
答:因为剩余模型没有在光源的视锥体内,所以没显示阴影,我们可以更改视锥体的大小位置来保证所有元素都显示阴影

//不一定都设置,根据需要设置
    dirLight.shadow.mapSize.width = 1024; // default
      dirLight.shadow.mapSize.height = 1024; // default
      dirLight.shadow.camera.near = 0.05; // default
      dirLight.shadow.camera.far = 400; // default
      dirLight.shadow.camera.top = 50
      dirLight.shadow.camera.right = 50
      dirLight.shadow.camera.left = -50
      dirLight.shadow.camera.bottom = -50

OrbitControls

平时开发调试代码,或者展示模型的时候,可以通过相机控件OrbitControls实现旋转缩放预览效果。

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键

通过鼠标滚轮、左右拖拽可以放大缩小、旋转查看模型,实际上是更改相机的位置
可以根据实际的需要设置,可以放大倍数,旋转的限制等。
因为在过程中一直更改模型,所以调用animate或者监听change保证更新界面

 // #引入扩展库OrbitCo
     import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

     let controls = new OrbitControls(camera, renderer.domElement); //创建控件对象
      controls.target.set(0, -1, 0); //相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
      controls.enableZoom = true;
      controls.update()
      controls.maxPolarAngle = Math.PI / 2  // 最大轨道高度为Π
      controls.minPolarAngle = 0  // 最大轨道高度为Π
      controls.autoRotate = true; //是否开启自动旋转


    const clock = new THREE.Clock();  //初始化时钟
    function animate() {
        // 通过相机 场景 将结果渲染出来
         let delta = clock.getDelta();
         controls.update(delta);
         requestAnimationFrame(animate);
         renderer.render(scene, camera);
      }

   animate()


 //如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
    renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件

stats

监听性能,计算three.js的渲染帧率(FPS),所谓渲染帧率(FPS)

//引入
import Stats from 'three/addons/libs/stats.module.js';

//创建stats对象
const stats = new Stats();
//stats.domElement:web页面上输出计算结果,一个div元素,
document.body.appendChild(stats.domElement);
// 渲染函数
function render() {
	//requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
	stats.update();
	renderer.render(scene, camera); //执行渲染操作
	requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}
render();

gui.js

就是一个前端js库,调试用,可以在gui面板更改值,具体的使用的时候可以再深入的了解,这里只是简单的介绍

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

const gui = new GUI();
//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';

// 通过GUI改变mesh.position对象的xyz属性
gui.add(ambient, 'intensity', 0, 2.0);

//更改模型位置
gui.add(mesh.position, 'x', 0, 180);
gui.add(mesh.position, 'y', 0, 180);
gui.add(mesh.position, 'z', 0, 180);

工具

模型查看

地址:gltf模型查看
可以查看图层、选中部分模型拖拽和模型一些属性值
在这里插入图片描述

在线Threejs地址

地址:国内地址
加载比官网快很多,不过是第三方培训机构的,不知道维护到什么时候,暂且用着。

gsap

地址:gsap官网
网上也有很多用tween的,看个人喜欢
动画库
例如:

   gsap.to(this.door.scale, {
        x: this.door.scale.x * 8, //scale拿到门的缩放值
        duration: 5,
        ease: "power1.inOut",
        onComplete: () => {

        }
      });

连续的动画

 const t1 = gsap.timeline();
    t1.to(that.door.scale, {
        x: that.door.scale.x / 8, //scale拿到门的缩放值
        duration: 5,
        ease: "power1.inOut",
        onComplete: () => {
          that.carMove = 'z'
        }
      });

      t1.to(model.position, {
        x: 8,
        y: 0,
        z: 24,
        duration: 5,
        ease: "linear",
        onComplete: () => {
          that.carMove = 'x'
          that.closeDoor()
          model.rotateY(Math.PI / 2) //旋转90度
        },
      })
      // 设置循环次数  -1为无限循环
      t1.repeat(-1);

      // 开始播放动画
      t1.play();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/346278.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【论文+App试玩+图像到视频】2311.Animate-anyone:上传1张图片为任何人制作动画(用于角色动画的一致且可控的图像到视频合成)(暂未开源)

项目主页:https://humanaigc.github.io/animate-anyone/ 论文: Animate Anyone: Consistent and Controllable Image-to-Video Synthesis for Character Animation 摩尔线程复现代码:https://github.com/MooreThreads/Moore-AnimateAnyone 原作者讲解&am…

Go 定时器:如何避免潜在的内存泄漏陷阱

这篇文章将探讨的是 Go 中如何高效使用 timer,特别是与select 一起使用时,如何防止潜在的内存泄漏问题。 引出问题 先看一个例子,我们在 Go 中的 select 使用定时器,实现为消息监听加上超时能力。 核心代码,如下所示…

linux clickhouse 安装

1、官网下载clickhouse安装包 下载地址, clickhouse分lts和stable版本,lts是长期版本,一般选择安装lts版本。 其中clickhouse-server是clickhouse服务,就是用来访问数据存储数据,clickhouse-client是用来通过命令访问数…

【操作系统和计网从入门到深入】(四)基础IO和文件系统

前言 这个专栏其实是博主在复习操作系统和计算机网络时候的笔记,所以如果是博主比较熟悉的知识点,博主可能就直接跳过了,但是所有重要的知识点,在这个专栏里面都会提到!而且我也一定会保证这个专栏知识点的完整性&…

Vue2 - keep-alive 作用和原理

目录 1&#xff0c;介绍和作用2&#xff0c;原理3&#xff0c;使用场景3.1&#xff0c;效果展示3.2&#xff0c;实现思路 1&#xff0c;介绍和作用 <!-- 非活跃的组件将会被缓存&#xff01; --> <keep-alive><component :is"activeComponent" />…

将AWS iot消息数据发送Kinesis Firehose Stream存向S3

观看此文章之前&#xff0c;请先学习AWS iot的数据收集&#xff1a; 使用Linux SDK客户端向AWS Iot发送数据-CSDN博客 1、工作原理&#xff1a; 1.1 规则 规则可让您的设备与 AWS 服务进行交互。分析规则并根据物品发送的消息执行操作。您可以使用规则来支持任务&#xff0…

前端开发提高效率的两大工具

一、浏览器中的开发者工具 怎么启动开发者工具&#xff1f; 在浏览器中按下F12或者鼠标右键点击检查 怎么利用&#xff08;常用的几点&#xff09;&#xff1f; 1、元素 点击标红的图标可以用于在页面选择元素&#xff0c;同时右侧会找到元素在前端代码中的位置 点击下方红…

2023 IoTDB Summit:中核武汉核电运行技术股份有限公司主管工程师方华建《IoTDB在核电数字化转型过程的应用实践》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

Pycharm终端显示PS而不显示虚拟环境venv

PS表示当前使用的是powershell.exe&#xff0c;如果你要显示虚拟环境名&#xff0c;则要改为cmd.exe 解决办法&#xff1a; 打开File-settings-Tools-Terminal-shell path 在文件中找到设置&#xff0c;在工具中找到终端 把第四个Shell路径设置为cmd.exe 3. 点击确定&#xf…

c#算法(10)——求点到直线的距离

前言 在上位机软件开发领域,特别是机器视觉领域,经常会遇到尺寸测量的场景,比如让我们求一个点到一条直线的距离,我们已知了直线上的两个点的坐标,然后又已知了直线外的一个点的坐标,那么如何求出该直线外的一点到直线的距离呢?本文就是来讲解如何求点到直线的距离的,…

uniapp page宽度设置为750rpx,子元素宽度100%,大小不一致

uniapp page宽度设置为750rpx&#xff0c;子元素宽度100%&#xff0c;大小不一致。 原因是我在page加了margin: 0 auto;去掉就正常了&#xff08;但是如果在超大屏幕还是会出现&#xff0c;我猜是使用rpx导致的&#xff0c;rpx渲染成页面时会转成精确到一个小数点几位数的rem&a…

【jetson笔记】vscode远程调试

vscode安装插件 vscode安装远程插件Remote-SSH 安装完毕点击左侧远程资源管理器 打开SSH配置文件 添加如下内容&#xff0c;Hostname为jetson IP&#xff0c;User为登录用户名需替换为自己的 Host aliasHostName 192.168.219.57User jetson配置好点击连接&#xff0c;控制台输…

详细Nginx和PHP-FPM的进程间通信使用

工作中考虑到PHP-FPM效率&#xff0c;发现PHP-FPM和NGINX的进程通信不止配置端口这一种方式:bowtie: Nginx和PHP-FPM的进程间通信有两种方式,一种是TCP,一种是UNIX Domain Socket. 其中TCP是IP加端口,可以跨服务器.而UNIX Domain Socket不经过网络,只能用于Nginx跟PHP-FPM都在同…

递归和尾递归(用C语言解斐波那契和阶乘问题)

很多人都对递归有了解&#xff0c;但是为尾递归很少&#xff0c;所以这次来专门讲一讲关于尾递归的一些问题。 什么是尾递归 如果一个函数中所有递归形式的调用都出现在函数的末尾&#xff0c;我们称这个递归函数是尾递归的。因为在一些题目的做法中&#xff0c;我们可以发现…

uniapp scroll-view用法[下拉刷新,触底事件等等...](4)

前言:可滚动视图区域。用于区域滚动 话不多说 直接上官网属性 官网示例 讲一下常用的几个 scroll 滚动时触发 scrolltoupper 滚动到顶部或左边&#xff0c;会触发 scrolltoupper 事件 scrolltolower 滚动到底部或右边&#xff0c;会触发 scrolltolower 事件 1.纵向滚动…

面向对象、封装、继承、多态、JavaBean

二、面向对象 什么是对象 什么是对象&#xff1f;之前我们讲过&#xff0c;对象就是计算机中的虚拟物体。例如 System.out&#xff0c;System.in 等等。然而&#xff0c;要开发自己的应用程序&#xff0c;只有这些现成的对象还远远不够。需要我们自己来创建新的对象。 1. 抽…

fpga外置flash程序烧录流程

Fpga外置FLASH程序烧录流程&#xff1a; step1&#xff1a; 打开vivado2019.2软件&#xff0c;找到hardware manager选项&#xff0c;进入该功能界面&#xff1b; Step2&#xff1a; 确定连接状态&#xff0c;当JTAG正确连接到板卡的调试插针后&#xff0c;会在状态窗口显示…

【嵌入式学习】网络通信基础-项目篇:简单UDP聊天室

源码已在GitHub开源&#xff1a;0clock/LearnEmbed-projects/chat 实现的功能 客户端功能&#xff1a; 上线发送登录的用户名[yes] 发送消息和接收消息[yes] quit退出 服务器端功能&#xff1a; 统计用户上线信息&#xff0c;放入链表中[yes] 接收用户信息并给其他用户发送消…

模型选择实战

我们现在可以通过多项式拟合来探索这些概念。 import math import numpy as np import torch from torch import nn from d2l import torch as d2l生成数据集 给定x&#xff0c;我们将使用以下三阶多项式来生成训练和测试数据的标签&#xff1a; max_degree 20 # 多项式的最…

Redis中BigKey的分析与优化

Redis中BigKey的分析与优化 Redis以其出色的性能和易用性&#xff0c;在互联网技术栈中占据了重要的地位。 但是&#xff0c;高效的工具使用不当也会成为性能瓶颈。在Redis中&#xff0c;BigKey是常见的性能杀手之一&#xff0c;它们会消耗过多的内存&#xff0c;导致网络拥塞…