threejs 基础知识点汇总

news2024/12/26 23:32:51

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,则必须在摄像机的变换发生任何手动改变后更新OrbitControls
    controls.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.offsetX
    y = 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;
  // 添加class
  div.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.coloradmin.cn/o/1581336.html

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

相关文章

OpenCV C++学习笔记

1.图像的读取与显示 1.1 加载并显示一张图片 #include<opencv2/opencv.hpp> #include<iostream>using namespace cv; using namespace std; int main(int argc,char** argv){Mat srcimread("sonar.jpg");//读取图像if(src.empty()){printf("Could…

大型语言模型如何助力推荐系统:综述研究

论文地址&#xff1a;https://arxiv.org/pdf/2306.05817.pdf 这篇论文主要探讨了推荐系统&#xff08;RS&#xff09;如何从大型语言模型&#xff08;LLM&#xff09;中获益。论文首先指出&#xff0c;随着在线服务和网络应用的快速发展&#xff0c;推荐系统已成为缓解信息过载…

解决使用php将excel数据导入数据库报错问题

今天在用 phpexcel 将数据xlxs数据导入到数据库发现一直报错 Array and string offset access syntax with curly braces is no longer supported 百度下发现PHP7.4后面版本,不再能够使用花括号来访问数组或者字符串的偏移&#xff0c;而我当前php版本是8.1 没办法根据他这个…

Linux 系统下对于 MySQL 的初级操作

由于公司老板想把早已封存的服务器陈年老码捣鼓一下&#xff0c;所以找了一个外援&#xff0c;我则是配合提供支持。但是过程并不顺利。至少 5 年以上的间隔&#xff0c;导致外援查看的时候发现很多代码和配置是缺失的&#xff0c;目前卡在数据库部分&#xff0c;而我这边就帮忙…

Mac 装 虚拟机 vmware、centos7等,21年网络安全面经分享

链接: https://pan.baidu.com/s/1oZw1cLyl6Uo3lAD2_FqfEw?pwdzjt4 提取码: zjt4 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 centos8 链接: https://pan.baidu.com/s/10KWpCUa2JkwcjYlJZVogKQ?pwdn99a 提取码: n99a 复制这段内容后打开百度网盘手机App&…

Nginx反向代理与Tomcat实现ssm项目前后端分离部署

Nginx nginx是一款http和支持反向代理的web服务器&#xff0c;以其优越的性能被广泛使用。以下是百度百科的介绍。 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.…

echarts 如何设置(dataZoom)多个图形的数据区域一起联动缩放响应

数据区域联动缩放需要用到 dataZoom 的专属事件 dispatchAction 实现多个数据区域联动缩放功能 <div style"width:100%;height:320px;" id"test01"></div> <div style"width:100%;height:320px;" id"test02"></…

AI大模型探索之路-应用篇2:Langchain框架ModelIO模块—数据交互的秘密武器

目录 前言 一、概述​​​​​​​ 二、Model 三、Prompt 五、Output Parsers 总结 前言 随着人工智能技术的不断进步&#xff0c;大模型的应用场景越来越广泛。LangChain框架作为一个创新的解决方案&#xff0c;专为处理大型语言模型的输入输出而设计。其中&#xff0c;…

苍穹外卖11(Apache ECharts前端统计,营业额统计,用户统计,订单统计,销量排名Top10)

目录 一、Apache ECharts【前端】 1. 介绍 2. 入门案例 二、营业额统计 1. 需求分析和设计 1 产品原型 2 业务规则 3 接口设计 2. 代码开发 3. 功能测试 三、用户统计 1. 需求分析和设计 1 产品原型 2 业务规则 3 接口设计 2. 代码开发 3. 功能测试 四、订单统…

Harmony鸿蒙南向驱动开发-DAC

DAC&#xff08;Digital to Analog Converter&#xff09;是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备。 DAC模块支持数模转换的开发。它主要用于&#xff1a; 作为过程控制计算机系统的输出通道&#xff0c;与执行器相连&#xff0c;实现对生产过程的自…

通过本机调试远端路由器非直连路由

实验目的&#xff1a;如图拓扑&#xff0c;通过本机电脑发&#xff0c;telnet调试远程AR4设备。 重点1&#xff1a;通过ospf路由协议配置拓扑网络&#xff0c;知识点&#xff1a;ospf配置路由器协议语法格式&#xff0c;area区域的定义&#xff0c;区域内网络的配置&#xff0…

蓝桥杯加训

1.两只塔姆沃斯牛&#xff08;模拟&#xff09; 思路&#xff1a;人和牛都记录三个数据&#xff0c;当前坐标和走的方向&#xff0c;如果人和牛的坐标和方向走重复了&#xff0c;那就说明一直在绕圈圈&#xff0c;无解 #include<iostream> using namespace std; const i…

idea keymap用eclipse的相关快捷键

idea快捷键用eclipse的方式 CtrlShiftR 搜索文件shiftshift 全部文件/类搜索CtrlH 全局搜索CtrlO 快速打开Outline大纲视图ctrle 查看recent窗口文件

排序算法,归并排序

归并排序是什么 归并排序&#xff08;Merge Sort&#xff09;是建立归并操作上的一种有效&#xff0c;稳定的排序算法&#xff0c;该算法是采用分治法的一个非常典型的应用 将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序&#xff0c;…

element UI table合并单元格方法

废话不多讲&#xff0c;直接上代码&#xff0c;希望能帮到需要的朋友 // 合并单元格function spanMethod({ row, column, rowIndex, columnIndex }) {//定义需要合并的列字段&#xff0c;有哪些列需要合并&#xff0c;就自定义添加字段即可const fields [declareRegion] // …

智能工业电脑在智慧电力中实现全程实时监控与调控

可视化编程工业电脑在化工、石油、电力等行业过程控制领域扮演着越来越重要的角色。这些基于ARM架构设计的嵌入式工业计算机凭借其高性能、低功耗以及出色的实时处理能力&#xff0c;有效提升了各行业生产过程的安全性和效率。 钡铼技术ARMxy系列采用嵌入式Linux 系统开发的产品…

Idea中 maven 下载jar出现证书问题

目录 1&#xff1a; 具体错误&#xff1a; 2&#xff1a; 忽略证书代码&#xff1a; 3&#xff1a; 关闭所有idea&#xff0c; 清除缓存&#xff0c; 在下面添加如上忽略证书代码 4&#xff1a;执行 maven clean 然后刷刷新依赖 完成&#xff0c;撒花&#xff01;&#x…

AIoT人工智能物联网----刷机、系统安装、示例、摄像头等

软件链接见文末 1. jetson nano硬件介绍 载板 模组卡座:放置核心板 micro SD卡接口:插SD卡,将操作系统写入SD卡,然后插入;建议至少为32GB。当然根据使用情况可以是64GB;卡的质量一定要好,读写速度快。之前买了同品牌128G的比64G的慢很多。所以大小合适就好M.2 Key E …

spark本地模拟多个task时如何启动多个Excutor

1、首先在9090端口下启动Excutor,作为第一个Excutor 2、然后修改9090端口为&#xff1a;9091&#xff0c;如下图点击Edit Configration 3、然后按下图操作 &#xff0c; 4、修改一下名字 5、点击apply&#xff0c;&#x1f197; 6、检查下面圈1是否是刚刚我们新建的MyExcutor(2…

基于PSO优化的CNN-GRU-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09;在时间序列中的应用 4.2 GRU网络 4.3 注意力机制&#xff08;Attention&#xff09; 5.算法完整程序工程 1.算法运行效果图预览 优化前 优化…