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.mzph.cn/news/813176.shtml

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

相关文章

jdk17 你还想用ScriptEngineManager来执行js代码?

今天要用java来执行配置表的js代码&#xff0c;用 ScriptEngine javaScriptEngine new ScriptEngineManager().getEngineByName(“javascript”); 一直抛异常&#xff1a;Cannot invoke “javax.script.ScriptEngine.eval(String)” because “javaScriptEngine” is null 网上…

Springboot集成Elastic-job

ElasticJob 通过弹性调度、资源管控、以及作业治理的功能&#xff0c;打造一个适用于互联网场景的分布式调度解决方案&#xff0c;并通过开放的架构设计&#xff0c;提供多元化的作业生态。 1 项目集成 1.1 springboot项目配置 导入依赖&#xff1a; <!--mysql连接基本配…

SSL证书添加与ICP备案,对于SpringBoot的要求

配置了SSL证书之后&#xff0c;在SpringBoot的resources文件夹里的application.properties会添加以下代码&#xff1a; server.port443 不需要添加server.address。不然会报错。 https类型的请求默认在Postman里面不可请求。 经过SSL证书处理的网页&#xff0c;链接中使默认…

LDRA Testbed软件静态分析_常见问题及处理

系列文章目录 LDRA Testbed软件静态分析_操作指南 LDRA Testbed软件静态分析_自动提取静态分析数据生成文档 LDRA Testbed软件静态分析_Jenkins持续集成_(1)自动进行静态分析的环境搭建 LDRA Testbed软件静态分析_Jenkins持续集成_(2)配置邮件自动发送静态分析结果 LDRA Testb…

水经微图IOS版5.2.0发布

随时随地&#xff0c;微图一下&#xff01; 水经微图&#xff08;简称“微图”&#xff09;IOS新版已上线。 在该版本中主要新增图层树节点排序功能、常规&#xff08;矩形、圆、椭圆、扇形&#xff09;绘制功能、地形夸张等主要功能。 当前版本 当前版本号为&#xff1a;5…

鹏哥C语言复习——数据存储

目录 版本差异&#xff1a; 数据类型&#xff1a; 进制表示&#xff1a; 大小端储存&#xff1a; 数据运算&#xff1a; 浮点型在内存中的存储&#xff1a; 版本差异&#xff1a; debug和release的区别&#xff1a; 在栈区开辟地址一般是先从高地址开辟 debug创建数组和单…

Project Euler_Problem 172_Few Repeated Digits_动态规划

原题目&#xff1a; 题目大意&#xff1a;18位数里头&#xff0c;有多少个数&#xff0c;对于每个数字0-9&#xff0c;在这18位里面出现均不超过3次 111222333444555666 布星~~ 112233445566778899 可以~~ 解题思路&#xff1a; 动态规划 代码: ll F[19][3000000];void …

DedeCMS 未授权远程命令执行漏洞分析

dedecms介绍 DedeCMS是国内专业的PHP网站内容管理系统-织梦内容管理系统&#xff0c;采用XML名字空间风格核心模板&#xff1a;模板全部使用文件形式保存&#xff0c;对用户设计模板、网站升级转移均提供很大的便利&#xff0c;健壮的模板标签为站长DIY自己的网站提供了强有力…

springboot发送邮件

很久之前就想写一个总结的&#xff0c;一直没写&#xff0c;今天刚好又碰见了发送邮箱验证码的需求&#xff0c;刚好记录一波 一.核心依赖如下&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-par…

谷歌推出适用于安卓设备的“Find My Device”网络,功能类似苹果Find My

谷歌今日推出了适用于安卓设备的“Find My Device”网络&#xff0c;其功能类似于苹果的“Find My”网络&#xff0c;旨在帮助用户定位丢失、被盗的安卓产品。 安卓的“Find My Device”网络可以利用数以亿计运行 Android 9 或更高版本的安卓设备&#xff0c;通过蓝牙信号追踪丢…

批量记录收支明细,高效记录当天的收支明细并查看每个支出占比,轻松掌握开销

在繁忙的现代生活中&#xff0c;我们时常因为琐碎的财务事务而分心。为了帮助您更好地管理财务&#xff0c;我们推出了这款智能财务助手&#xff0c;让您可以高效记录每天的收支明细&#xff0c;并轻松掌握每个支出的占比。从此告别混乱&#xff0c;让财务管理变得简单明了 第…

gym界面修改

资料&#xff1a;https://blog.csdn.net/weixin_46178278/article/details/135962782 在gym环境中使用mujoco的时候&#xff0c;有一个很难受的地方&#xff0c;界面上没有实时显示动作空间和状态空间状态的地方。 gym自己原始带的环境是用pygame画的图&#xff0c;所以在定义…

【前端】es-drager 图片同比缩放 缩放比 只修改宽 只修改高

【前端】es-drager 图片同比缩放 缩放比 ES Drager 拖拽组件 (vangleer.github.io) 核心代码 //初始宽 let width ref(108)//初始高 let height ref(72)//以下两个变量 用来区分是单独的修改宽 还是高 或者是同比 //缩放开始时的宽 let oldWidth 0 //缩放开始时的高 let o…

JWT重放漏洞如何攻防?你的系统安全吗?

大家好&#xff0c;我是石头~ 在数字化浪潮席卷全球的今天&#xff0c;JSON Web Token&#xff08;JWT&#xff09;作为身份验证的利器&#xff0c;已成为众多Web应用的首选方案。 然而&#xff0c;正如硬币有两面&#xff0c;JWT的强大功能背后也隐藏着潜在的安全风险&#xf…

2024mathorcup数学建模D题思路分析-量子计算在矿山设备配置及运营中的建模应用

# 1 赛题 D 题 量子计算在矿山设备配置及运营中的建模应用 随着智能技术的发展&#xff0c;智慧矿山的概念越来越受到重视。越来越多的 设备供应商正在向智慧矿山整体解决方案供应商转型&#xff0c;是否具备提供整体 解决方案的能力&#xff0c;也逐步成为众多矿山设备企业的核…

UVC摄像头在虚拟机Ubuntu16.04使用的正确姿势

前言&#xff1a;在Windows使用UVC摄像头是正常的&#xff0c;但在虚拟机Ubuntu中使用可以识别到&#xff0c; 但是一直没有数据出来&#xff0c;一度怀疑是摄像头不行&#xff0c;后来经过仔细研究&#xff0c;才发现是虚拟机usb设置有点问题。一、虚拟机USB设置USB 3.0,不然没…

【C++类和对象】上篇

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

字节码文件的组成

字节码文件的组成 字节码文件的组成1 以正确的姿势打开文件2 字节码文件的组成2.1 基本信息2.2 常量池2.3 字段2.4 方法2.5 属性 3 字节码常用工具3.1 javap3.2 jclasslib插件3.3 Arthas 4 字节码常见指令 字节码文件的组成 1 以正确的姿势打开文件 字节码文件中保存了源代码…

Git 解决分支冲突

一、前言 一直习惯于 add commit push 的三步走&#xff0c;偶然间看到了一个评论说在 push 之前还有一个 pull&#xff0c;小小的疑问就埋在了我的心里。于是我就先了解了 pull 的工作原理&#xff0c;就是先拉取代码&#xff08;fetch&#xff09;再合并分支&#xff08;mer…

matlab使用教程(42)—常见的二维图像绘制方法

这个博客用于演示如何在 MATLAB 中创建曲线图、条形图、阶梯图、误差条形图、极坐标图、针状图、散点图。 1.曲线图 plot 函数用来创建 x 和 y 值的简单线图。 x 0:0.05:5; y sin(x.^2); figure plot(x,y) 运行结果&#xff1a; 线图可显示多组 x 和 y 数据。 x 0:0.05:…