webgl_decals

ThreeJS 官方案例学习(webgl_decals)

1.效果图

在这里插入图片描述

2.源码

<template>
	<div>
		<div id="container"></div>
	</div>
</template>
<script>
// 光线投射相关代码 https://threejs.org/docs/index.html#api/zh/core/Raycaster
import * as THREE from 'three';
// 导入控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 引入房间环境,创建一个室内环境
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
// 导入性能监视器
import Stats from 'three/examples/jsm/libs/stats.module.js';
// 导入gltf载入库、模型加载器
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
// 引入模型解压器
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
// 引入贴花几何体
import { DecalGeometry } from 'three/examples/jsm/geometries/DecalGeometry.js';
//GUI界面
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import gsap from 'gsap';
export default {
	data() {
		return {
			container: null, //界面需要渲染的容器
			scene: null,	// 场景对象
			camera: null, //相机对象
			renderer: null, //渲染器对象
			controller: null,	// 相机控件对象
			stats: null,// 性能监听器
			mixer: null,//动画混合器
			mesh: null,//导入的模型
			gui: null,//GUI界面
			clock: new THREE.Clock(),// 创建一个clock对象,用于跟踪时间
			raycaster: new THREE.Raycaster(),//光线投射,用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)
			params: null,
			intersection: {
				intersects: false,//是否存在相交的点
				point: new THREE.Vector3(),//相交部分的点(世界坐标)的坐标
				normal: new THREE.Vector3()//相交的面
			},
			mouse: new THREE.Vector2(),//创建鼠标位置。创建一个二维向量为后面 Raycaster 实例调用 .setFromCamera 方法做准备
			intersects: [],//物体和射线的焦点(光线投射与鼠标拾取时相交的物体数组)
			mouseHelper: null,//鼠标定位帮助器
			line: null,//鼠标定位帮助线条
			position: new THREE.Vector3(),//贴花投影器的位置
			orientation: new THREE.Euler(),//贴花投影器的朝向
			size: new THREE.Vector3(10, 10, 10),// 贴花投影器的尺寸
			decals: [],//点击时创建的贴花几何体集合
		};
	},
	mounted() {
		// gui参数(贴花参数)
		this.params = {
			minScale: 10,//最大缩放值
			maxScale: 20,//最小缩放值
			rotate: true,//贴花是否旋转
			clear: () => {
				this.removeDecals();
			}
		}
		this.init()
		this.animate()  //如果引入了模型并存在动画,可在模型引入成功后加载动画
		window.addEventListener("resize", this.onWindowSize)
		let moved = false;//判断是否移动状态
		// controller 控制器存在操作时(物体处于移动中时),moved置为true,不进行相关操作
		this.controller.addEventListener('change', () => { moved = true; });
		window.addEventListener("pointerdown", () => { moved = false })
		window.addEventListener("pointerup", (event) => {
			if (moved === false) {
				this.checkIntersection(event.clientX, event.clientY)
				if (this.intersection.intersects) this.shoot()
			}
		})

		window.addEventListener('pointermove', this.onPointerMove);
	},
	beforeUnmount() {
		console.log('beforeUnmount===============');
		// 组件销毁时置空
		this.container = null
		this.scene = null
		this.camera = null
		this.renderer = null
		this.controller = null
		this.stats = null
		this.mixer = null
		this.model = null//导入的模型
	},
	methods: {
		/**
		* @description 初始化
		 */
		init() {
			this.container = document.getElementById('container')
			this.setScene()
			this.setCamera()
			this.setRenderer()
			this.setController()
			this.addHelper()
			// this.setPMREMGenerator()
			this.setLight()
			this.setGltfLoader()
			this.addStatus()
		},
		/**
		 * @description 创建场景
		 */
		setScene() {
			// 创建场景对象Scene
			this.scene = new THREE.Scene()
			// 设置场景背景
			// this.scene.background = new THREE.Color(0xbfe3dd);
		},
		/**
		 * @description 创建相机
		*/
		setCamera() {
			// 第二参数就是 长度和宽度比 默认采用浏览器  返回以像素为单位的窗口的内部宽度和高度
			this.camera = new THREE.PerspectiveCamera(60, this.container.clientWidth / this.container.clientHeight, 1, 1000)
			// 设置相机位置
			this.camera.position.z = 120
			// 设置摄像头宽高比例
			this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
			// 设置摄像头投影矩阵
			this.camera.updateProjectionMatrix();
			// 设置相机视线方向
			this.camera.lookAt(new THREE.Vector3(0, 0, 0))// 0, 0, 0 this.scene.position
			// 将相机加入场景
			this.scene.add(this.camera)
		},
		/**
		 * @description 创建渲染器
		 */
		setRenderer() {
			// 初始化渲染器
			this.renderer = new THREE.WebGLRenderer({
				antialias: true,// 设置抗锯齿
				// logarithmicDepthBuffer: true,  // 是否使用对数深度缓存
			})
			// 设置渲染器宽高
			this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
			// 设置渲染器的像素比
			this.renderer.setPixelRatio(window.devicePixelRatio);
			// 是否需要对对象排序
			this.renderer.sortObjects = false;
			// 将渲染器添加到页面
			this.container.appendChild(this.renderer.domElement);
		},
		/**
		 * @description 添加创建控制器
		 */
		setController() {
			this.controller = new OrbitControls(this.camera, this.renderer.domElement);
			// 控制缩放范围
			this.controller.minDistance = 50;
			this.controller.maxDistance = 200;
			//是否开启右键拖拽
			// this.controller.enablePan = false;
			// 阻尼(惯性)
			// this.controller.enableDamping = true; //启用阻尼(惯性)
			// this.controller.dampingFactor = 0.04; //阻尼惯性有多大
			// this.controller.autoRotate = true; //自动围绕目标旋转
			// this.controller.minAzimuthAngle = -Math.PI / 3; //能够水平旋转的角度下限。如果设置,其有效值范围为[-2 * Math.PI,2 * Math.PI],且旋转角度的上限和下限差值小于2 * Math.PI。默认值为无穷大。
			// this.controller.maxAzimuthAngle = Math.PI / 3;//水平旋转的角度上限,其有效值范围为[-2 * Math.PI,2 * Math.PI],默认值为无穷大
			// this.controller.minPolarAngle = 1; //能够垂直旋转的角度的下限,范围是0到Math.PI,其默认值为0。
			// this.controller.maxPolarAngle = Math.PI - 0.1; //能够垂直旋转的角度的上限,范围是0到Math.PI,其默认值为Math.PI。
			// 修改相机的lookAt是不会影响THREE.OrbitControls的target的
			// 由于设置了控制器,因此只能改变控制器的target以改变相机的lookAt方向
			// this.controller.target.set(0, 0.5, 0); //控制器的焦点
		},
		/**
		 * @description 创建辅助坐标轴
		 */
		addHelper() {
			// 模拟相机视锥体的辅助对象
			let helper = new THREE.CameraHelper(this.camera);
			// this.scene.add(helper);
			//创建辅助坐标轴、轴辅助 (每一个轴的长度)
			let axisHelper = new THREE.AxesHelper(150);  // 红线是X轴,绿线是Y轴,蓝线是Z轴
			// this.scene.add(axisHelper)
			// 坐标格辅助对象
			let gridHelper = new THREE.GridHelper(100, 30, 0x2C2C2C, 0x888888);
			// this.scene.add(gridHelper);

			this.mouseHelper = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 10), new THREE.MeshNormalMaterial());
			this.mouseHelper.visible = false;
			this.scene.add(this.mouseHelper);
		},
		/**
		 * @description 给场景添加环境光效果
		 */
		setPMREMGenerator() {
			// 预过滤的Mipmapped辐射环境贴图
			const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
			this.scene.environment = pmremGenerator.fromScene(new RoomEnvironment(this.renderer), 0.04).texture;
		},
		/**
		 * @description 设置光源
		 */
		setLight() {
			// 环境光
			const ambientLight = new THREE.AmbientLight(0x666666);
			this.scene.add(ambientLight);
			// 平行光
			const dirLight1 = new THREE.DirectionalLight(0xffddcc, 3);
			dirLight1.position.set(1, 0.75, 0.5);
			this.scene.add(dirLight1);

			const dirLight2 = new THREE.DirectionalLight(0xccccff, 3);
			dirLight2.position.set(- 1, 0.75, - 0.5);
			this.scene.add(dirLight2);
		},
		/**
		 * @description 创建性能监听器
		*/
		addStatus() {
			// 创建一个性能监听器
			this.stats = new Stats();
			// 将性能监听器添加到容器中
			this.container.appendChild(this.stats.dom);
		},
		/**
		* @description 添加创建模型
		*/
		setGltfLoader() {
			let that = this
			// 实例化gltf载入库
			const loader = new GLTFLoader();
			// 实例化draco载入库
			const dracoLoader = new DRACOLoader();
			// 添加draco载入库
			dracoLoader.setDecoderPath("/draco/gltf/");
			// 添加draco载入库
			loader.setDRACOLoader(dracoLoader);

			//创建鼠标定位帮助线条
			const geometry = new THREE.BufferGeometry();
			geometry.setFromPoints([new THREE.Vector3(), new THREE.Vector3()]);
			this.line = new THREE.Line(geometry, new THREE.LineBasicMaterial());
			this.scene.add(this.line);


			// 添加模型
			const textureLoader = new THREE.TextureLoader()//纹理贴图加载器
			const map = textureLoader.load(require("../../../public/model/gltf/LeePerrySmith/Map-COL.jpg"));
			map.colorSpace = THREE.SRGBColorSpace;
			const specularMap = textureLoader.load('../../../public/models/gltf/LeePerrySmith/Map-SPEC.jpg');
			const normalMap = textureLoader.load('../../../public/models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg');
			loader.load('/model/gltf/LeePerrySmith/LeePerrySmith.glb', (gltf) => {
				that.mesh = gltf.scene.children[0];
				that.mesh.material = new THREE.MeshPhongMaterial({
					specular: 0x111111,//材质的高光颜色
					map: map,//颜色贴图
					specularMap: specularMap,//镜面反射贴图
					// normalMap: normalMap,//法线贴图的纹理
					shininess: 25,//高亮的程度
				});
				that.scene.add(that.mesh)
				that.mesh.scale.set(10, 10, 10)
			}, undefined, (err => {
				console.error(err)
			}))


			const gui = new GUI();
			gui.add(this.params, 'minScale', 1, 30);
			gui.add(this.params, 'maxScale', 1, 30);
			gui.add(this.params, 'rotate');
			gui.add(this.params, 'clear');
			gui.open();
		},
		/**
		* @description pointermove 窗口事件
		*/
		onPointerMove(event) {
			if (event.isPrimary) {
				this.checkIntersection(event.clientX, event.clientY);
			}
		},
		/**
		* @description 计算物体和射线之间的焦点
		*/
		// 光线投射相关代码 https://threejs.org/docs/index.html#api/zh/core/Raycaster
		checkIntersection(x, y) {
			if (this.mesh === undefined) return
			// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
			this.mouse.x = (x / window.innerWidth) * 2 - 1;
			this.mouse.y = - (y / window.innerHeight) * 2 + 1;
			// 通过摄像机和鼠标位置更新射线
			this.raycaster.setFromCamera(this.mouse, this.camera);
			// 检测所有在射线与物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个。
			// intersectObjec()第三个参数 - 结果的目标数组, 如果设置了这个值,则在每次调用之前必须清空这个数组(例如:array.length = 0;)
			this.raycaster.intersectObject(this.mesh, false, this.intersects);
			// 如果存在相交点
			if (this.intersects.length > 0) {
				const p = this.intersects[0].point//相交部分的点(世界坐标)
				this.mouseHelper.position.copy(p);
				this.intersection.point.copy(p);

				const n = this.intersects[0].face.normal.clone();//相交的面
				n.transformDirection(this.mesh.matrixWorld);
				n.multiplyScalar(10);
				n.add(this.intersects[0].point);

				this.intersection.normal.copy(this.intersects[0].face.normal);
				this.mouseHelper.lookAt(n);

				// 设置line焦点
				const positions = this.line.geometry.attributes.position;
				positions.setXYZ(0, p.x, p.y, p.z);
				positions.setXYZ(1, n.x, n.y, n.z);
				positions.needsUpdate = true;

				this.intersection.intersects = true;
				//intersectObjec() 清空数组
				this.intersects.length = 0;
			} else {
				this.intersection.intersects = false;
			}
		},
		/**
		* @description 创建贴花几何体
		*/
		shoot() {
			// 设置贴花几何体的位置、朝向、尺寸
			this.position.copy(this.intersection.point);
			this.orientation.copy(this.mouseHelper.rotation);
			if (this.params.rotate) this.orientation.z = Math.random() * 2 * Math.PI;
			const scale = this.params.minScale + Math.random() * (this.params.maxScale - this.params.minScale);
			this.size.set(scale, scale, scale);

			// 加载纹理贴图
			const textureLoader = new THREE.TextureLoader()//纹理贴图加载器
			const decalDiffuse = textureLoader.load('/textures/decal/decal-diffuse.png');
			decalDiffuse.colorSpace = THREE.SRGBColorSpace;
			const decalNormal = textureLoader.load('/textures/decal/decal-normal.jpg');

			// 设置贴花几何体材质
			const decalMaterial = new THREE.MeshPhongMaterial({
				specular: 0x444444,//材质的高光颜色
				map: decalDiffuse,//颜色贴图
				normalMap: decalNormal,//法线贴图的纹理
				normalScale: new THREE.Vector2(1, 1),//法线贴图对材质的影响程度
				shininess: 30,//高亮的程度
				transparent: true,//材质是否透明,存在map时为true
				depthTest: true,//是否在渲染此材质时启用深度测试
				depthWrite: false,//渲染此材质是否对深度缓冲区有任何影响
				polygonOffset: true,//是否使用多边形偏移
				polygonOffsetFactor: - 4,//多边形偏移系数
				wireframe: false,//渲染为平面多边形
			});
			const material = decalMaterial.clone();
			//设置随机颜色
			material.color.setHex(Math.random() * 0xffffff);
			// 引入贴花物体 DecalGeometry - 贴花几何体
			const m = new THREE.Mesh(new DecalGeometry(this.mesh, this.position, this.orientation, this.size), material);
			// 创建贴花几何体集合数组
			this.decals.push(m);
			// 在场景中添加贴花
			this.scene.add(m);
		},
		/**
		* @description 清除贴花几何体集合
		*/
		removeDecals() {
			this.decals.forEach((d) => {
				this.scene.remove(d);
			});
			this.decals.length = 0;
		},
		/**
		 * @description 监听屏幕的大小改变,修改渲染器的宽高,相机的比例
		*/
		// 窗口变化
		onWindowSize() {
			// 更新摄像头
			this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
			// 更新摄像机的投影矩阵
			this.camera.updateProjectionMatrix();
			//更新渲染器
			this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
			// 设置渲染器的像素比
			this.renderer.setPixelRatio(window.devicePixelRatio)
		},
		/**
		* @description 动画执行函数
		*/
		animate() {
			const delta = this.clock.getDelta();
			// 引擎自动更新渲染器
			requestAnimationFrame(this.animate);
			//update()函数内会执行camera.lookAt(x, y, z)
			this.controller.update(delta);
			// 更新性能监听器
			this.stats.update();
			// 重新渲染场景
			this.renderer.render(this.scene, this.camera);
		},
	},
};
</script>
<style>
#container {
	position: absolute;
	width: 100%;
	height: 100%;
}
</style>


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

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

相关文章

电路方案分析(十九)快速响应过流事件检测电路

快速响应过流事件检测电路 1.设计需求2.设计方案3.设计说明4.仿真验证 tips&#xff1a;方案参考来自TI参考设计&#xff0c;仅供学习交流使用。 1.设计需求 2.设计方案 这是一种快速响应单向电流检测解决方案&#xff0c;通常称为过流保护 (OCP)&#xff0c;可提供 < 2μ…

安徽某高校数据挖掘作业6

1 根据附件中year文件&#xff0c;编辑Python程序绘制年销售总额分布条形图和年净利润分布条形图&#xff0c;附Python程序和图像。 2 根据附件中quarter和quarter_b文件&#xff0c;编辑Python程序绘制2018—2020年销售额和净利润折线图&#xff0c;附Python程序和图像。 3 …

全志D1s软件入门之Tina Linux烧写教程

烧写 Tina Linux 烧写&#xff0c;即将编译打包好的固件下载到设备 烧写方式简介 全志平台为开发者提供了多种多样的烧写方式和烧写工具&#xff1a; &#xff08;1&#xff09; PhoenixSuit&#xff1a;基于Windows的系统的烧写工具&#xff0c;是最常用的烧写工具&#x…

【AI大模型】Transformers大模型库(一):Tokenizer

目录 一、引言 二、Tokenizer 2.1 概述 2.2 主要功能 2.3 代码示例 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库&#xff0c;为huggingface上数以万计的预训练大模型提供预测、训练等服务。 &#x1f917; Transformers 提供了数以千计的预训…

AI绘画入门指南!一遍就会!AI绘画Stable Diffusion新手入门教程

我们尝试了一段时间Midjourney&#xff0c;发现其对图片的可控性较弱。于是研究起了Stable Diffusion。 SD的主要优势在于开源&#xff0c;因为开源会有很多无私的大佬分享自己的模型、插件及脚本等&#xff0c;让SD有了更丰富的扩展。在画面统一性和更像本人方面要比MJ容易实…

104.网络游戏逆向分析与漏洞攻防-装备系统数据分析-筛选与装备有关的数据包

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

学生成绩管理系统(大一大作业)

功能 实现添加&#xff0c;排序&#xff0c;修改&#xff0c;保存等功能 库函数 #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<string.h> 头文件 #define functioncreate(major) void major##compare(mana mn){\int i,j,s…

必应bing国内广告怎样开户投放呢?

企业都在寻找高效、精准的营销渠道以扩大品牌影响力&#xff0c;提升市场占有率&#xff0c;作为全球第二大搜索引擎&#xff0c;微软旗下的必应Bing凭借其卓越的搜索技术和庞大的用户基础&#xff0c;成为了众多企业拓展市场的首选广告平台。在中国&#xff0c;必应Bing广告以…

ESP32S3——多线程

一、环境&#xff1a; 平台&#xff1a;arduino IDE 或 VS Code PlatformIO 皆可。 我的是后者&#xff0c;具体为&#xff1a; 框架&#xff1a;VS PlatformIO Arduino 二、硬件准备&#xff1a; 一个esp32s3 本文用到的是U0RXD&#xff08;GPIO44 &#xff09;与U0TXD…

运动会信息管理系统(Springboot+MySQL)

本课题旨在实现对运动会信息的全面管理&#xff0c;提供用户友好的界面和高效的操作体验。系统的基础功能包括运动员报名比赛、比赛成绩查询、资讯留言等。为了确保系统的高扩展性和稳定性&#xff0c;选用主流的开发技术&#xff0c;实现规范的项目结构和高效的性能。 技术选型…

RAR文件忘记密码怎么办?解密方法大盘点

随着信息技术的飞速发展&#xff0c;数据的安全性和保密性越来越受到重视。RAR作为一种常用的压缩文件格式&#xff0c;因其压缩效率高、支持文件加密等特点而广受欢迎。然而&#xff0c;当我们忘记了RAR压缩包的密码&#xff0c;或者需要解密他人分享的加密RAR文件时&#xff…

如何统一集成全域数据?三个点讲清楚多源异构数据融合

关于多源异构数据的融合问题&#xff0c;主要涉及以下三个关键点&#xff1a; 1、多源异构数据融合的必要性 2、多源异构数据融合的挑战 3、多源异构数据融合的解决方案 首先&#xff0c;我们来说多源异构数据融合的必要性。 随着以大数据为代表的信息技术快速发展&#xf…

HTTP相关面试题

1. HTPP基本概念 HTTP是超文本传输协议。本质上就是一个可以传输图片、视频、文字的计算机与计算机之间的协议 1.1. HTTP常见的状态码 2XX状态码: 主要用于表示已经服务器已经成功的处理了请求 [200 ok ]: 是最常见的状态码,表示我们请求成功且响应内容(响应头body)已经收到…

笔记 | 软件工程02:软件工程概述

1 软件工程产生背景 1.1 历史发展 1960s的个体作坊式软件开发带来的问题 1.2 软件开发需要解决的问题 代码规模增长带来的影响&#xff1a; 1.3 软件开发面临的挑战 指挥信息系统中的软件&#xff1a;规模大、质量要求高 装备中嵌入式软件系统&#xff1a;规模大、质量要求…

调用smc为什么就能直接切换到ATF?

快速链接: . &#x1f449;&#x1f449;&#x1f449;Trustzone/TEE/安全 面试100问-目录 &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】:联系方式-加入交流群 ----联系方式-加入交流群 个人博客笔记导读目录(全部) 背景 插播一个小插曲&#…

大数据在金融行业的深度应用与未来展望

一、引言 随着信息技术的迅猛发展,大数据已经成为推动金融行业创新的重要力量。从精准营销、个性化服务到风险管理和产品创新,大数据的应用正在不断重塑金融行业的格局。本文将深入探讨大数据在金融行业的深度应用,分析其特征特点、解决方案以及面临的挑战与机遇,并展望其…

深度学习 --- stanford cs231 编程作业(assignment1,Q2: SVM分类器)

stanford cs231 编程作业之SVM分类器 写在最前面&#xff1a; 深度学习&#xff0c;或者是广义上的任何学习&#xff0c;都是“行千里路”胜过“读万卷书”的学识。这两天光是学了斯坦福cs231n的一些基础理论&#xff0c;越往后学越觉得没什么。但听的云里雾里的地方也越来越多…

STM32——hal_SPI_(介绍)

SPI&#xff08;串行外围设备接口&#xff09;是一种高速的、全双工、同步的通信协议&#xff0c;通常用于短距离通信&#xff0c;尤其是在嵌入式系统中与各种外围设备进行通信。SPI接口由摩托罗拉公司推出&#xff0c;由于其简单和灵活的特性&#xff0c;它被广泛用于多种应用…

云计算百科:类型、服务与业务优势一网打尽

了解云计算的权威指南是至关重要的&#xff0c;掌握云计算的类型、服务、用途以及它们如何为组织带来好处尤为重要。 假设某公司的员工正在办公室工作&#xff0c;突然间火警响起&#xff0c;原来IT部门的主服务器机房着火了&#xff0c;所有服务器都受到了影响。这一消息引发…

【机器学习-09】 | Scikit-Learn工具包进阶指南:Scikit-Learn工具包之高斯混合sklearn.mixture模块研究

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…