threejs 基础知识点汇总

threejs 基础知识点汇总

之前写了几篇博文,但是我觉得写的不好,我今天再补充一篇还不好的,把基础知识点汇总一下,不写运行的代码了,只写关键代码,但是看了之前我写的那几篇,看这篇的话问题其实不大。嘿嘿!现学现卖!

threejs 简介

Three.js是一个流行的JavaScript库,用于在浏览器中创建和显示3D图形。它基于WebGL,一个浏览器支持的3D图形API,使得开发者能够在网页上创建复杂的3D场景和交互体验。

Three.js提供了丰富的功能和工具,让开发者能够轻松地创建3D对象、设置灯光、添加动画、处理用户交互等。它支持多种3D格式的导入,如OBJ、GLTF等,也支持自定义的材质和着色器。

官网:https://threejs.org/
官方文档:https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene

threejs 安装

如果你正在使用 Node.js 和 npm(Node Package Manager),你可以通过 npm 安装Three.js。在终端或命令提示符中运行以下命令:

npm install three --save

这将安装最新版本的Three.js。如果你想要安装特定版本的Three.js,你可以指定版本号,例如:

npm install three@0.128.0

threejs 使用先创建一个Dom

如果将Threejs渲染的三维效果展示到电脑页面,页面需要存在一个挂载三维模型的DoM节点,因此我们需要在页面展示三维模型的地方创建一个DOM,来挂载三维模型场景。

<!--创建一个div,用于展示三维模型-->
<div ref="edThreeBox" id="ed-three-box"></div>//获取dom
let dom = this.$refs.edThreeBox
或
let dom = document.getElementByld('ed-three-box')

Three.js 创建场景Scene

把三维场景Scene(opensnewwindow)对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。后期所有涉及展示的三维模型,包括但不限于模型、光线、辅助线全部添加到场景中方可进行展示。

// 引入场景
import {Scene}from "three";//创建3D场景对象Scene 
const scene = new Scene();

Three.js创建透视投影相机PerspectiveCamera

Threejs提供了正投影相机OrthographicCamera(opens new window)和透视投影相机PerspectiveCamera(opens new window)。大家比较常用的透视投影相机PerspectiveCamera,这里也直说透视投影相机。

透视投影相机PerspectiveCamera本质上就是在模拟人眼观察这个世界的规律。

//引入透视投影相机
import { PerspectiveCamera ↓from "three";//创建透视投影相机
let camera = new PerspectiveCamera(50, dom.offsetWidth / dom.offsetHeight, 1, 2000);//设置相机位置
camera.position.set(5, 5, 5);//设置相机的拍摄坐标,就是他的目标点位置
camera.lookAt(new Vector3(0, 0, 0));

Three.js创建渲染器WebGLRenderer

WebGLRenderer是Three.js中的一个关键组件,它的主要作用是将场景和相机渲染成二维图片并显示在指定的HTML元素(通常是元素)上。WebGLRenderer实现了WebGL的接口,并利用GPU硬件加速来提高渲染性能。

//导入渲染器
import {WebGLRenderer }from "three";
//初始化渲染器
let renderer = new WebGLRenderer();
//把渲染结果挂载到dom节点
dom.appendchild(renderer.domElement);
//设置渲染器大小
renderer.setSize(dom.offsetWidth, dom.offsetHeight, true);
//执行渲染操作
renderer.render(scene, camera);

Three.js 模型

场景、相机初始化完成之后,可以向场景中添加一个简单的模型进行展示。在此之前需要了解三个概念:几何体(物体形状)材质(物体外观)网格模型(物体)

可以简单理解一下:我们创建的模型,就是一个网格模型(物体),比如一个箱子;这个箱子长啥样、有多大,就是几何体(物体形状)控制;这个箱子是什么颜色、粗糙度这种样式是由材质(物体外观)控制。也可以简单理解成“物体是由几何体和材质构成的”,最后添加到场景的是一个物体。

常用几何体

在这里插入图片描述

常用材质

在这里插入图片描述

Three.js 几何体 Geometry

Three.js提供了各种各样的几何体APl,用来表示三维物体的几何形状。

//导入几何体
import {BoxGeometry }from "three";
球体或者: 
import * as Three from "three";//创建一个立方体几何体圆锥
const geometry = new BoxGeometry(2, 2, 2)

创建一个立方体几何体,长高宽分别为:2、2、2。

常见几何体

//BoxGeometry:长方体
const geometry = new BoxGeometry(100, 100, 100);// SphereGeometry:球体
const geometry = new SphereGeometry(50);// CylinderGeometry:圆柱
const geometry = new CylinderGeometry(50,50,100);// PlaneGeometry:矩形平面
const geometry = new PlaneGeometry(100,50);//CircleGeometry:圆形平面
const geometry = new CircleGeometry(50);

设置材质:

// 导入材质,这种材质不受光照的影响。
import { MeshBasicMaterial } from "three";//创建一个材质对象Material
const material = new MeshBasicMaterial({color: 0xff0000,  //0xff0000设置材质颜色为红色
});

设置单面可见:

// 设置模型材质两面可见
const material = new MeshBasicMaterial({color: 0xff0000,  // 0xff0000设置材质颜色为红色side: DoubleSide,  // 设置模型两面可见
});

单双面设置:

在这里插入图片描述

如果是平面,我们根据需要可以设置背面可见,或者两面可见。但是对于立方体,如果不需要进入模型内部查看的话,没必要设置双面可见,设置双面可见,将会影响渲染效率,消耗计算机性能。

Three.js 材质Material

如果你想定义物体的外观效果,比如颜色,就需要通过材质Material相关的API实现。

// 导入材质,这种材质不受光照的影响。
import { MeshBasicMaterial } from "three";//创建一个材质对象Material
const material = new MeshBasicMaterial({color: 0xff0000,  //0xff0000设置材质颜色为红色
});

创建一个材质,设置材质的颜色为红色。

Three.js 网格模型Mesh

实际生活中有各种各样的物体,在threejs中可以通过网格模型Mesh (opens new window)表示一个虚拟的物体,比如一个箱子、一座房子。

// 导入网格模型
import { Mesh } from "three";// 两个参数分别为几何体geometry、材质material
const mesh = new Mesh(geometry, material);   //网格模型对象Mesh

创建了几何体、材质、网格模型后,需要将创建的网格模型添加到场景就可以在页面展示三维模型。场景存在一个 add() 方法,可通过该方法将模型添加到场景。

// 将网格模型添加到场景
scene.add(mesh);

Three.js 渲染场景

通过上面步骤操作完成之后发现页面是黑色的,渲染不出效果,原因是渲染的问题。我们还没有对它进行真正的渲染。为此,我们需要使用一个被叫做“渲染循环”(render loop)或者“动画循环”(animate loop)的东西。

function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();

在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒),但是不绝对,理想情况下是60次每秒,电脑性能不好或者是代码写的处理逻辑太多消耗太多性能的话,肯定到不了60帧。

在这里插入图片描述

Three.js 渲染场景抗锯齿

通过之前的代码添加的模型可以正常展示了,但是仔细看的话,在立方体边线渲染的时候会产生一种锯齿纹。

在这里插入图片描述

我们可以通过代码设置来优化一下实现抗锯齿效果。在初始化渲染器的时候可以设置参数,其中一个参数是 antialias ,该参数的作用是是否执行抗锯齿。默认为 false。我们开启一下。

// 初始化渲染器
let renderer = new WebGLRenderer({
antialias:true,  // 开启抗锯齿
});

除此之外,我们还可以设置渲染像素比为设备像素比来优化锯齿效果。

// 设置像素比为设备的像素比,防止渲染模糊
renderer.setPixelRatio(window.devicePixelRatio);

但是这些方式都是优化,不是彻底解决。系统默认是关闭的,需要通过上面代码手动开启,既然系统默认关闭就说明开启是有代价的哈,对的,就是开启会影响性能,一个模型两个模型还好,模型多了,可能会出现卡顿的问题嗷。

Three.js 深度冲突

什么是深度冲突,下面创建两个平面,都默认加载到坐标原点:

// 创建平面
const plantGeometry = new THREE.PlaneGeometry(80, 80);
const plantMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
let plane = new THREE.Mesh(plantGeometry, plantMaterial);// 创建平面1
const plantGeometry1 = new THREE.PlaneGeometry(120, 120);
const plantMaterial1 = new THREE.MeshBasicMaterial({ color: 0x00ffff, side: THREE.DoubleSide });
let plane1 = new THREE.Mesh(plantGeometry1, plantMaterial1);

看一下效果:

在这里插入图片描述
两个平面显示的若隐若现,为啥呢?当两个面间隙很小,就可能出现深度冲突。从纯理论的角度,你能分清0和0.0000…0000001的大小,但是实际上,电脑GPU精度是有限的,电脑分不清谁在前谁在后,不知道应该先渲染谁,就会出现这个情况。

Three.js 解决深度冲突

解决方案一:拉开间距

let plane1 = new THREE.Mesh(plantGeometry1, plantMaterial1);
plane1.position.z = 0.1

给两个平面之间添加一点距离:

在这里插入图片描述

解决方案二:设置webgl渲染器设置对数深度缓冲区

let renderer = new Three.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true  // 设置深度缓冲区
});

有一点要注意,当两个面间隙过小,或者重合,你设置webgl渲染器深度缓冲区也是无效的,这种方式也是对深度冲突的优化,不是解决。

Three.js 三维坐标系

在Three.js中,渲染三维模型时,当我们使用 scene.add 将模型添加到场景中后,模型默认添加在坐标系原点,也就是 (0,0,0) 处。没错,在Three.js中是存在坐标系的,坐标系存在x轴、y轴、z轴。怎么定义的呢,我们可以使用辅助坐标系进行辅助查看。

// 导入三维坐标系
import { AxesHelper } from "three";
// 实例化一个三维坐标轴,辅助坐标轴长度为 5
const axesHelper = new AxesHelper(5);
// 添加到三维场景
this.scene.add(axesHelper);

在这里插入图片描述

看到出现了三根线,我们添加的模型没有设置位置的话,模型默认加载到坐标原点,沿蓝色线为Z轴正方向,沿红色线为X轴正方向,沿绿色线位Y轴正方向。

在这里插入图片描述

注意,Three.js中坐标系没有明确的单位,但是模型设计工具可能有,所以说在设计模型的时候需要与美术提前确定好单位,比如渲染房子的单位可能是米,渲染铅笔可能是厘米,切记单位不要混了。

Three.js 模型位置设置

我们如果不想让立方体添加在坐标原点我们可以通过位置设置,修改模型的初始位置。

// 修改模型位置
mesh.position.set(3, 0, 0);  // x轴设置为3
// 或者
mesh.position.x = 3

在这里插入图片描述

除去位置可以设置之外,还可以对他的缩放、旋转进行设置。具体操作可以查看官方文档。

Three.js 光源对物体的影响

实际生活中物体表面的明暗效果是会受到光照的影响,比如晚上不开灯,你就看不到物体,灯光比较暗,物体也比较暗。在threejs中,咱们用网格模型Mesh模拟生活中物体,所以threejs中模拟光照Light对物体表面的影响,就是模拟光照Light对网格模型Mesh表面的影响。

在 Three.js 提供的材质里面,有可以受光照影响的材质,有不受光照影响的材质。

之前编写的代码,我们没有在场景中添加光线,模型依旧可以看见,是因为我们使用了MeshBasicMaterial 材质,他是一个不受光照影响的材质,如果我们使用其他材质,则立方体就不会显示,因为没有添加光线进行照射。

在这里插入图片描述

使用一个受光照影响的材质:

	//创建一个材质对象Material,材质受光照影响const material = new MeshLambertMaterial({color: 0xff0000,  //0xff0000设置材质颜色为红色});

看一下效果:

在这里插入图片描述

小立方体已经看不见了,因为场景里面没有光线,所以说看不见。

Three.js 光源

当使用MeshLambertMaterial材质时,会受到光线的影响, 我们代码里面如果没有设置光线,则使用MeshLambertMaterial材质修饰的模型不可见,这个时候,我们添加光线后,便可以看见。

Three.js提供了多种模拟生活中光源的API。

在这里插入图片描述

光源特点:

在这里插入图片描述

Three.js 点光源

点光源 PointLight (opens new window) 可以类比为一个发光点,就像生活中一个灯泡,以灯泡为中心向四周发射光线。

// 导入点光源
import { PointLight } from "three";// 创建点光源并设置白色光、光照强度200、最远照射距离10
const light = new PointLight(0xffffff, 200, 10);// 设置点光源的位置,x轴5,y轴5,z轴3
light.position.set(5, 5, 3);// 将点光源添加到场景
scene.add(light);

把点光源想象为一个电灯泡,在3D空间中,放的位置不同,模型的渲染效果就不一样。

注意光源位置尺寸大小:如果你希望光源照在模型的外表面,那你就需要把光源放在模型的外面。

在这里插入图片描述

Three.js 相机控件OrbitControls

平时开发调试代码,或者展示模型的时候,可以通过相机控件 OrbitControls 实现旋转缩放预览效果。就是可以像百度地图一样,通过鼠标来旋转场景、缩放场景、移动场景。

// 导入相机控件(轨道控制器)
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';// 创建相机控件
const controls = new OrbitControls(camera, renderer.domElement);

【默认使用规则】

  • 旋转:拖动鼠标左键 THREE.MOUSE.ROTATE;
  • 缩放:滚动鼠标中键 THREE.MOUSE.DOLLY;
  • 平移:拖动鼠标右键 THREE.MOUSE.PAN;

可以通过设置修改鼠标键的功能项:

controls.mouseButtons = {       // 设置鼠标功能键(轨道控制器)LEFT: null,           // 左键无功能MIDDLE: MOUSE.DOLLY,  // 中键缩放RIGHT: MOUSE.ROTATE   // 右键旋转
}

在相机控件变化的时候,我们可以使用监听事件,来获取当前场景或者是相机数据,这样方便我们调试相机视角。比如,当我们想设置相机拍摄某个视角,但是有无法确定项目应该设置的最佳位置时,我们可以通过相机控件手动移动到目标位置,然后就可以通过变化事件监听,看到当前相机位置。

 	controls.addEventListener('change', () => {// 浏览器控制台查看相机位置变化console.log('camera.position', camera.position);});

看一下打印的结果:

在这里插入图片描述

Three.js 布局自适应

在上面案例中,我们想让挂载的DOM自适应页面的变化,比如我们的DOM使用百分比布局,当浏览器窗体拖宽的时候 three.js 渲染的区域不能很好的自适应。

// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {// 重置渲染器输出画布canvas尺寸renderer.setSize(dom.offsetWidth, dom.offsetHeight);// 全屏情况下:设置观察范围长宽比aspect为窗口宽高比camera.aspect = dom.offsetWidth / dom.offsetHeight;// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵camera.updateProjectionMatrix();// 如果使用了OrbitControls,则必须在摄像机的变换发生任何手动改变后更新OrbitControlscontrols.update()
};

Three.js 克隆.clone() 和 复制.copy()

克隆 .clone()、复制 .copy() 是threejs很多对象都具有的方法,比如三维向量对象Vector3、网格模型Mesh、几何体、材质。
克隆 .clone() 简单说就是复制一个和原对象一样的新对象,但他不是深度拷贝。
复制 .copy() 简单说就是把一个对象属性的属性值赋值给另一个对象。

对材质的影响:

  • 当一个场景中模型使用同一套材质时,修改其中任意一个模型的材质,其余材质均被修改。
  • 当一个场景中模型使用各自创建的材质,修改其中任意一个模型材质,不会对其他模型材质造成影响。 当使用 clone
  • 克隆某一个模型时,其材质是共享的原模型材质,修改材质后对原模型材质有影响。

Three.js 建模

对于简单的立方体、球体等模型,你可以通过three.js的几何体相关API快速实现,不过复杂的模型,比如一辆轿车、一栋房子、一个仓库,一般需要通过3D建模软件来实现。

3D美术常用的三维建模软件,比如Blender、3dmax、C4D、maya 等。

在这里插入图片描述
一个公司对于三维开发的分工:

  • 3D美术:使用三维建模软件绘制3D模型,导出gltf等常见格式。
  • 2D美术:根据三维模型设计贴图。
  • WebGL开发:加载解析三维软件导出的三维模型。比如使用Blender三维建模软件导出gltf格式模型,然后再通过threejs加载三维模型。

Three.js GLTF模型解释

GLTF格式是新2015发布的三维模型格式,随着物联网、WebGL、5G的进一步发展,会有越来越多的互联网项目Web端引入3D元素,你可以把GLTF格式的三维模型理解为.jpg、.png格式的图片一样,现在的网站,图片基本是标配,对于以后的网站来说如果需要展示一个场景,使用3D来替换图片表达也是很正常的事情。图片有很多格式,对于三维模型自然也是如此,Web开发的时候图片会有常用格式,对于Web3D开发也一样,肯定会根据需要选择一个常见的大家都熟悉的格式,随时时间的发展,GLTF必然称为一个极为重要的标准格式。

不仅three.js,其它的WebGL三维引擎cesium、babylonjs都对gltf格式有良好的的支持。

在这里插入图片描述

Three.js 加载Gltf模型

GLTFLoader就是three.js的一个扩展库,专门用来加载gltf格式模型加载器。

// 导入模型加载器
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 实例化一个gltf的加载器对象
let loader = new GLTFLoader();
// 加载gltf模型
loader.load("/static/models/model/model.gltf", (gltf) => {scene.add(gltf.scene);
})

在这里插入图片描述
展示效果:

在这里插入图片描述

Three.js 雾化效果

看上面加载的模型,环境黑色和模型之间的边界,棱角分明,我们可以使用雾化效果修饰一下,让边界不是很明显:

// 场景开启雾化效果
scene.fog = new Three.Fog(0x000000, 650, 900);

在这里插入图片描述

场景的雾化效果,是针对于相机。三个参数是:雾化颜色起始位置结束位置

如果渲染器背景为黑色,无特殊情况下,建议使用相同的颜色做为雾化效果。

Three.js 射线控制器Raycaster

光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。

在这里插入图片描述

在二维平面,点击一个按钮很简单,因为屏幕是平面的,页面也是平面的,根据 X、Y 就可以定位元素位置。

在这里插入图片描述

在三维,渲染的是一个立体的场景,我们就不能单纯通过电脑屏幕的 X、Y 来获取元素位置,因为三维存在 Z 轴。

在这里插入图片描述

在现实场景中,我们如果想让同行的朋友关注远处的一座山,我们只需伸手指向那座山,朋友就会根据当时的场景,结合你看的方向,结合你手指的方向,他就可以知道你说的是那座山。

在这个过程中,并没有直接把手指怼到山上,朋友依旧可以理解我们的意思。同理在三维场景中,我们想要获取某个物体,并不需要让鼠标怼到模型上。

在threejs中,提供了射线控制器,可以帮我们实现类似的效果。首先引入射线控制器:

// 实例化射线控制器
let raycaster = new Three.Raycaster();

射线发射器 Raycaster 会根据鼠标在二维屏幕中点击的位置,结合三维场景和相机数据,从屏幕向鼠标点击的方向发出一条射线,把被射线穿过模型返回成一个列表,列表的顺序就是射线穿过模型的先后顺序。

所以我们照着某个模型点过去,射线一定会穿过小方块,当然可能还有其他的模型一起被穿过了,但是第一个穿过的肯定目标小方块。

我们首先需要知道鼠标是在屏幕哪个地方点击的,获取鼠标在页面点击的坐标,这个很简单:

// 创建鼠标点击事件获取鼠标点击位置
renderer.domElement.addEventListener("click", event => {x = event.offsetXy = event.offsetY
})

射线控制器创建完成,它有一个 setFromCamera 方法,用来通过像机和鼠标位置更新射线,需要传入两个参数,分别是 在标准化设备坐标中鼠标的二维坐标场景

raycaster.setFromCamera(mouse, camera)

问题来了!!

setFromCamera 方法的相机没有疑义,但是在标准化设备坐标中鼠标的二维坐标 有点问题。

在监听鼠标点击事件获取的坐标,是相对于屏幕的。标准化设备坐标中鼠标的二维坐标 是 threejs 视角的鼠标位置,这个位置和我们通过点击事件获取出来的相对于屏幕的鼠标位置是不一样的。

在这里插入图片描述

对于 threejs 而言,他的原点就是屏幕宽度的一半和屏幕高度的一半。所以:横轴: (x - width / 2) / (width / 2)
纵轴: (height / 2 - y) / (height / 2)化简一下就是:x / width * 2 - 1
-y * 2 / height + 1

通过上面化简的公式,就可以将获取到的鼠标坐标转化为 threejs 坐标:

let Sx = event.clientX;    // 获取鼠标x轴坐标
let Sy = event.clientY;    // 获取鼠标y轴坐标
let x = (Sx / dom.offsetWidth) * 2 - 1;   // 转换x坐标
let y = -(Sy / dom.offsetHeight) * 2 + 1;  // 转换y坐标
let raycaster = new Three.Raycaster();  // 射线控制器
raycaster.setFromCamera(new Three.Vector2(x, y), camera);    // 通过像机和鼠标位置更新射线

Raycaster 射线控制器还有一个方法叫 intersectObjects,他的作用就是获取被射线穿过的模型数量。他需要传递一个参数,是检测和射线相交的一组物体。

// 获取射线穿过模型列表
const intersection = raycaster.intersectObjects(scene.children)

他会返回啥呢,他返回的是被射线穿过的模型顺序,是一个数组,按照被射线穿过的模型顺序组装起来的数组,就是先穿过谁,谁就在前面。但是要注意了,场景中添加的东西,都会被检测到,比如辅助线这些。

  if (intersects.length > 0) {        // 如果存在穿过的模型if (chooseMesh) {       // 如果之前有设置了颜色的模型chooseMesh.material.color.set(0xffffff);      // 恢复之前颜色}chooseMesh = intersects[0].object;      //  修改被设置的模型为射线穿过的第一个模型chooseMesh.material.color.set(0xffff00);       // 将穿过的第一个模型设置为黄色}

在这里插入图片描述

Three.js CSS 2D渲染器 CSS2DRenderer

通过 CSS2DRenderer 可以把HTML元素作为标签标注三维场景。但是注意一点,就是他只支持100%的浏览器缩放比例正常运行。在此过程中,需要将两个库导入一下:CSS2DRendererCSS2DObject

导入的方式很简单:

import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';

如果你希望将三维物体和基于HTML的标签相结合,则这一渲染器将十分有用。在这里,各个DOM元素也被包含到一个 CSS2DObject 实例中,并被添加到场景图中。

它允许开发者将HTML元素作为标签标注到三维场景中,这对于在三维地图或者图形中添加文本标签特别有用。CSS2DRenderer是CSS3DRenderer的简化版本,它主要支持位移变换,这意味着可以使用它来在三维空间中定位HTML元素,但不支持旋转或缩放等其他三维变换。

CSS2DRenderer 创建

// 创建一个CSS2的渲染器
function createCSS2DRendererFun(dom) {let labelRenderer = new CSS2DRenderer();// 设置渲染器大小labelRenderer.setSize(dom.offsetWidth, dom.offsetHeight);labelRenderer.domElement.style.position = 'absolute';labelRenderer.domElement.style.top = '0px';labelRenderer.domElement.style.pointerEvents = 'none';dom.appendChild(labelRenderer.domElement);return labelRenderer
}

上面是一个方法,方法中创建了一个CSS2DRenderer渲染器,设置渲染器的大小、属性、挂载位置,最后返回。

这个渲染器和渲染三维场景的渲染器不是一个渲染器,你可以简单的理解成,我们把这个渲染器盖在了三维场景渲染器的上方,用来渲染我们后期需要添加的 HTML 标签。这也就解释了为什么设置他的 postition 等属性。

因为案例的三维场景占据整个屏幕,所以在这里直接挂载到了dom上面了。

CSS2DObject 介绍

CSS2DObject 是 Three.js 中用于在3D场景里渲染HTML元素的类。

  • HTML元素包装:它允许开发者将HTML元素包装成可以在3D场景中渲染的对象。
  • 场景连接:通过CSS2DObject,HTML元素可以与three.js中的场景连接,这意味着元素可以根据物体的位置和场景的相机位置自动定位和渲染。
  • 位置设置:开发者可以通过设置CSS2DObject的position属性来定义HTML元素在3D空间中的位置,也可以获取Mesh(网格)的世界坐标来确定标签的位置。
  • 信息展示:CSS2DObject常与CSS2DRenderer一起使用,用于在Three.js中绘制2D效果的标签,这对于展示一些场景相关信息非常有用。

CSS2DObject 创建

// 创建html标签
function tag(name) {// 创建div标签let div = document.createElement('div');// 设置标签显示内容div.innerHTML = name;// 添加classdiv.classList.add("tag")// 创建CCS2DObject对象var label = new CSS2DObject(div);// 返回CSS2DObject对象return label
}

上面提供了一个方法用来创建一个HTML标签,设置标签的展示内容,并且最终导出一个 CSS2DObject 对象。

当然HTML的标签我们依旧可以通过 CSS 样式进行修饰。下面是 CSS 对其样式进行修改的代码:

.tag {background-color: rgba(0, 0, 0, 0.4);color: #fff;font-size: 14px;padding: 4px 10px;border-radius: 3px;border: 1px solid #00ffff;box-sizing: border-box;
}

模型添加2D标签

  let group = gltf.scene.getObjectByName('粮仓'); group.traverse((obj) => {if (obj.type === 'Mesh') {let label = tag(obj.name)let v3 = new Three.Vector3();obj.getWorldPosition(v3);if (obj.parent.name === '平房仓') {v3.y += 20} else if (obj.parent.name === '立筒仓') {v3.y += 39} else if (obj.parent.name === '浅圆仓') {v3.y += 23}label.position.copy(v3);model.add(label);}})

在我们拿到粮仓模型之后可以通过递归遍历,拿到所有的模型,之后给相应的粮仓模型添加 label。

getWorldPosition:用于获取某个对象在世界坐标系中的位置。

场景展示HTML标签

在场景中展示 HTML 标签和渲染三维一样。首先创建一个渲染器:

  // 添加CSS2DRenderer渲染器const labelRenderer = createCSS2DRendererFun(dom);

在每一帧切换的时候更新一下:

	let animate = () => {...      labelRenderer.setSize(dom.offsetWidth, dom.offsetHeight);labelRenderer.render(scene, camera);....requestAnimationFrame(animate);}animate()

效果展示

在这里插入图片描述

标签已经添加到场景进行展示。我们可以发现标签不随相机自动变换大小,并且标签正面始终朝向镜头。

Three.js CSS 3D渲染器 CSS3DRenderer

CSS3DRenderer 是 Three.js 库中的一个组件,用于在 WebGL 场景中渲染 HTML 元素。它允许开发者将DOM元素转换为三维对象,并使用CSS变换来实现三维效果。

导入的方式很简单:

import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';

CSS3DRenderer, CSS3DObject 的用法,和 2D 几乎是完全一样的,我们简单写一下,直接之前 2D 的修改就可以了,那我直接贴代码,不做赘述。

Three.js CSS 3D渲染器初始化

// 创建一个CSS3的渲染器
function createCSS3DRendererFun(dom) {let labelRenderer = new CSS3DRenderer();labelRenderer.setSize(dom.offsetWidth, dom.offsetHeight);labelRenderer.domElement.style.position = 'absolute';labelRenderer.domElement.style.top = '0px';labelRenderer.domElement.style.pointerEvents = 'none';dom.appendChild(labelRenderer.domElement);return labelRenderer
}

CSS3DObject 对象初始化

// 创建html标签
function tag3D(name) {let div = document.createElement('div');div.innerHTML = name;div.classList.add("tag")var label = new CSS3DObject(div);div.style.display = 'none';label.scale.set(0.2, 0.2, 1)label.rotateY(Math.PI / 2)return label
}

CSS3DRenderer 使用

  let group = Mathia('粮仓');group.traverse((obj) => {if (obj.type === 'Mesh') {let label = tag3D(obj.name)let v3 = new Three.Vector3();obj.getWorldPosition(v3);if (obj.parent.name === '平房仓') {v3.y += 20} else if (obj.parent.name === '立筒仓') {v3.y += 39} else if (obj.parent.name === '浅圆仓') {v3.y += 23}label.position.copy(v3);model.add(label);}})
// 添加CSS3DRenderer渲染器
const labelRenderer = createCSS3DRendererFun(dom);

展示效果

在这里插入图片描述

标签已经添加到场景进行展示。我们可以发现标签随相机自动变换大小,不会一直朝向镜头。

CSS3DSprite 精灵

CSS3DSprite 是 3D 中的一个精灵,怎么理解呢,他和 CSS3DObject 的特性一样,但是他会自动朝向镜头。这玩意儿也是需要导入的,导入很简单。

import { CSS3DRenderer, CSS3DObject, CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js';

他和3D标签唯一的区别就是,在创建标签的时候,不生成 CSS3DObject 了,而是使用 CSS3DSprite。

var label = new CSS3DSprite(div);

直接看一下效果:

在这里插入图片描述

Gsap 动画

three.js 结合 Gsap(GreenSock Animation Platform)可以创建丰富的3D动画效果。

Gsap是一个功能强大的JavaScript动画库,它支持各种动画需求,包括CSS、SVG、Canvas,以及WebGL等。

官网:https://gsap.com/

在这里插入图片描述

首先我们需要通过 npm 安装 gsap。安装很简单,一行命令结束:

npm install gsap

使用案例:

		gsap.to(camera.position, {x: -1.5,y: 8.0,z: -15.2,duration: 1.5,ease: "power1.inOut"})

就这些了!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

群晖NAS使用Docker部署Potopea在线图片编辑工具并实现公网访问

文章目录 1. 部署Photopea2. 运行Photopea3. 群晖安装Cpolar4. 配置公网地址5. 公网访问测试6. 固定公网地址 本文主要介绍如何在群晖NAS使用Docker部署Potopea在线图片编辑工具&#xff0c;并结合cpolar内网穿透实现公网环境可以远程访问本地部署的Potopea. Photopea是一款强大…

伺服电机的惯性

一、伺服电机的惯性 伺服电机的惯性主要指电机及其连接的负载的惯性。它是通过将物体的质量与其距离旋转轴的平方相乘得到的。对于伺服电机来说&#xff0c;惯性体现了电机和负载对速度和加速度变化的阻力程度&#xff0c;即其惯性越大&#xff0c;对速度和加速度变化的阻…

人工智能_大模型023_AssistantsAPI_01_OpenAI助手的创建_API的调用_生命周期管理_对话服务创建---人工智能工作笔记0159

先来说一下一些问题: 尽量不要微调,很麻烦,而且效果需要自己不断的去测试. 如果文档中有图表,大量的图片去分析就不合适了. 是否用RAG搜索,这个可以这样来弄,首先去es库去搜能直接找到答案可以就不用去RAG检索了,也可以设置一个分,如果低于60分,那么就可以去进行RAG检索 微…

看不起的行业,其实比工作 赚的多

1、烧烤&#xff0c;只要你敢干&#xff0c;一年的利润是普通人五年的工资&#xff0c;日入2000。 2、翻新二手手机&#xff0c;深圳华强北好的时间段一天能卖出几千台&#xff0c;日入1000。 3、大学食堂开个小卖部&#xff0c;一个月就能挣个大千&#xff0c;日入1500。 4…

基于Spring Boot与Vue的智能化学生心理咨询评估系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

计算机网络:网络层的路由选择协议

网络层的 路由选择协议 路由表 从下图的三个简单的拓扑结构所示&#xff0c;如果其中一条链路断了&#xff0c;静态路由就通不了&#xff0c;断了。但是使用动态路由可以动态调届选择策略。 静态路由和动态路由的区别对比和特点 路由选择协议 自治系统AS 内部网关协议RI…

MySQL高级(索引结构Hash,为什么InnoDB存储引擎选择使用B+tree索引结构?)

目录 1、Hash索引结构 2、Hash索引特点 3、存储引擎支持 4、为什么InnoDB存储引擎选择使用Btree索引结构&#xff1f; 1、Hash索引结构 哈希索引就是采用一定的hash算法&#xff0c;将键值换算成新的hash值&#xff0c;映射到对应的槽位上&#xff0c;然后存储在hash表中。 如…

SpringBoot第一个hello world项目

文章目录 前言一、Spring Boot是什么&#xff1f;二、使用步骤1. 创建项目2.书写测试 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了…

【机器学习】深入解析机器学习基础

在本篇深入探讨中&#xff0c;我们将揭开机器学习背后的基础原理&#xff0c;这不仅包括其数学框架&#xff0c;更涵盖了从实际应用到理论探索的全方位视角。机器学习作为数据科学的重要分支&#xff0c;其力量来源于算法的能力&#xff0c;这些算法能够从数据中学习并做出预测…

【JavaWeb】Tomcat服务器

目录 动态网站动态网站的特点 程序架构B/S与C/S的比较B/S技术的工作原理URL 什么是Web服务器 Web服务器、服务端、服务器的区别和联系什么是TomcatTomcat服务器的安装与配置解压缩版本Tomcat的配置添加系统变量&#xff0c;名称为CATALINA_HOME&#xff0c;值为Tomcat的安装目录…

C/C++中局部变量static用法实例

1. 普通局部变量存储于进程栈空间&#xff0c;使用完毕会立即释放&#xff0c;静态局部变量使用static修饰符定义&#xff0c;即使在声明时未赋初值&#xff0c;编译器也会把它初始化为0&#xff0c;并且静态局部变量存储于进程的全局数据区&#xff0c;即使函数返回&#xff0…

解密项目管理专业术语:十大名词背后的实战技巧

项目管理是一门综合学科&#xff0c;涵盖了一系列方法、技能和工具。今天为大家带来项目管理的十大专业术语&#xff0c;它们分别是项目范围、利益相关者管理、工作分解结构&#xff08;WBS&#xff09;、里程碑、风险管理、资源分配、关键路径法&#xff08;CPM&#xff09;、…

Word·VBA文档合并

目录 1&#xff0c;复制法&#xff0c;不保留原文档格式2&#xff0c;复制法&#xff0c;保留原文档格式3&#xff0c;插入法&#xff0c;保留原文档格式 之前的文章《WordVBA实现邮件合并》虽然可以生成邮件合并文档结果&#xff0c;但是不能像《python实现word邮件合并》一样…

计算机网络-运输层

运输层 湖科大计算机网络 参考笔记&#xff0c;如有侵权联系删除 概述 运输层的任务&#xff1a;如何为运行在不同主机上的应用进程提供直接的通信服务 运输层协议又称端到端协议 运输层使应用进程看见的好像是在两个运输层实体之间有一条端到端的逻辑通信信道 运输层为应…

鸿蒙原生应用已超4000个!

鸿蒙原生应用已超4000个&#xff01; 来自 HarmonyOS 微博近期消息&#xff0c;#鸿蒙千帆起# 重大里程碑&#xff01;目前已有超4000个应用加入鸿蒙生态。从今年1月18日华为宣布首批200多家应用厂商正在加速开发鸿蒙原生应用&#xff0c;到3月底超4000个应用&#xff0c;短短…

【算法详解】二分查找

1. 二分查找算法介绍 「二分查找算法&#xff08;Binary Search Algorithm&#xff09;」&#xff1a;也叫做 「折半查找算法」、「对数查找算法」。是一种在有序数组中查找某一特定元素的搜索算法。 基本算法思想&#xff1a;先确定待查找元素所在的区间范围&#xff0c;在逐步…

k8s_入门_命令详解

命令详解 kubectl是官方的CLI命令行工具&#xff0c;用于与 apiserver进行通信&#xff0c;将用户在命令行输入的命令&#xff0c;组织并转化为 apiserver能识别的信息&#xff0c;进而实现管理k8s各种资源的一种有效途径 1. 帮助 2. 查看版本信息 3. 查看资源对象等 查看No…

C语言——文件管理

文件&#xff1a;即磁盘上的文件&#xff0c;使用文件可以将数据直接存放在电脑的硬盘上&#xff0c;做到数据持久化。 在程序设计中&#xff0c;按文件的功能划分&#xff0c;将文件分为程序文件与数据文件 程序文件 程序文件包括源文件&#xff08;.c&#xff09;&#xff0…

MySQL - 基础三

11、事务管理 CURD不加控制&#xff0c;会有什么问题&#xff1f; 当客户端A检查还有一张票时&#xff0c;将票卖掉&#xff0c;还没有执行更新数据库时&#xff0c;客户端B检查了票数&#xff0c;发现大于0&#xff0c;于是又卖了一次票。然后A将票数更新回数据库。这是就出现…

C语言从入门到实战————文件操作

目录 前言 1. 为什么使用文件&#xff1f; 2. 什么是文件&#xff1f; 2.1 程序文件 2.2 数据文件 2.3 文件名 3. ⼆进制文件和文本文件&#xff1f; 4. 文件的打开和关闭 4.1 流和标准流 4.1.1 流 4.1.2 标准流 4.2 文件指针 4.3 文件的打开和关闭 5. 文…