使用THREE.js实现材质球,材质球,定制材质

news2024/11/10 1:11:29

项目需求,需要自定义材质球,方便使用封装成了类,可以使用在各种项目

1.效果展示

 2:实现代码

使用方式,传入初始化DOM,和初始化材质配置即可

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { LightProbeGenerator } from "three/addons/lights/LightProbeGenerator.js";

class AdvancedMaterialSphere {
  constructor(dom, materialOptions = {}, materialType = "MeshStandardMaterial") {
    this.dom = dom; // 保存DOM引用
    this.materialOptions = materialOptions; // 保存初始材质选项
    this.materialType = materialType; // 保存初始材质类型

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

    // 确保DOM元素有正确的宽高
    if (!dom || dom.clientWidth === 0 || dom.clientHeight === 0) {
      throw new Error("无效的DOM元素或尺寸。");
    }

    // 计算相机的长宽比
    const aspectRatio = dom.clientWidth / dom.clientHeight;

    // 创建相机
    this.camera = new THREE.PerspectiveCamera(45, aspectRatio, 0.1, 10000);
    // 设置相机位置
    this.camera.position.set(10, 0, 0);
    this.camera.lookAt(0, 0, 0);

    // 创建渲染器
    this.renderer = new THREE.WebGLRenderer({ antialias: true }); // 启用抗锯齿
    this.renderer.setSize(dom.clientWidth, dom.clientHeight); // 设置渲染器大小
    this.renderer.setPixelRatio(window.devicePixelRatio); // 设置像素比
    this.renderer.toneMapping = THREE.NoToneMapping; // 色调映射
    dom.appendChild(this.renderer.domElement); // 将渲染器的DOM元素添加到文档中

    // 光探测器
    this.lightProbe = new THREE.LightProbe();
    this.scene.add(this.lightProbe);

    // 添加方向光
    this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
    this.directionalLight.position.set(800, 800, 800); // 设置平行光的位置
    this.scene.add(this.directionalLight);

    // 纹理贴图加载器
    this.texLoader = new THREE.TextureLoader();
    this.cubeTexLoader = new THREE.CubeTextureLoader();

    // 加载环境贴图
    this.loadEnvironmentMap("/pisa/", ["px.png", "nx.png", "py.png", "ny.png", "pz.png", "nz.png"], () => {
      this.createSphere(); // 加载完成后创建球体
    });

    // 设置相机控件轨道控制器
    const controls = new OrbitControls(this.camera, this.renderer.domElement);
    controls.target.set(0, 0, 0); // 设置轨道控制器的观察目标为球体
    controls.update(); // 更新控件
    controls.minDistance = 10; // 设置最小距离
    controls.maxDistance = 50; // 设置最大距离
    controls.enablePan = false; // 禁用平移

    // 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
    controls.addEventListener("change", () => {
      this.renderer.render(this.scene, this.camera);
    });

    // 绑定resize事件处理器以适应窗口大小变化
    window.addEventListener("resize", () => this.onWindowResize(), false);

    // 立即调用一次以适应初始大小
    this.onWindowResize();

    // 开始渲染循环
    this.animate();
  }

  // 加载环境贴图
  loadEnvironmentMap(path, files, onLoad) {
    this.scene.background = this.cubeTexLoader.setPath(path).load(files, (cubeTexture) => {
      this.scene.background = cubeTexture; // 设置场景背景为加载的立方体贴图
      this.lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture)); // 从立方体贴图生成光探测器
      if (onLoad) onLoad();
    });
  }

  // 创建球体
  createSphere() {
    const geometry = new THREE.SphereGeometry(1, 32, 32); // 创建球体几何体
    this.sphere = new THREE.Mesh(geometry, this.initMaterial(this.materialOptions)); // 创建球体网格并加入场景
    this.sphere.position.set(0, 0, 0); // 设置球体位置
    this.scene.add(this.sphere); // 将球体加入场景
  }

  // 初始化材质
  initMaterial(matOptions) {
    const materialParams = { envMapIntensity: 0.5, ...matOptions };

    // 动态检查并设置材质贴图
    this.setMaterialMap(matOptions, materialParams, "map");
    this.setMaterialMap(matOptions, materialParams, "metalnessMap");
    this.setMaterialMap(matOptions, materialParams, "roughnessMap");
    this.setMaterialMap(matOptions, materialParams, "normalMap");
    this.setMaterialMap(matOptions, materialParams, "bumpMap");
    this.setMaterialMap(matOptions, materialParams, "displacementMap");
    this.setMaterialMap(matOptions, materialParams, "emissiveMap");
    this.setMaterialMap(matOptions, materialParams, "alphaMap");
    this.setMaterialMap(matOptions, materialParams, "lightMap");
    this.setMaterialMap(matOptions, materialParams, "aoMap");

    // 根据指定的材质类型创建材质
    this.material = new THREE[this.materialType](materialParams);
    return this.material;
  }

  // 设置材质贴图
  setMaterialMap(matOptions, materialParams, mapType) {
    if (matOptions[mapType]) {
      materialParams[mapType] = this.setMap(matOptions[mapType]);
    }
  }

  // 设置纹理贴图
  setMap(file) {
    if (!file) return null; // 如果文件不存在,返回null
    return this.texLoader.load(file); // 加载并返回纹理
  }

  // 更新材质
  updateMaterial() {
    if (this.sphere) {
      this.sphere.material.dispose(); // 释放旧材质
      this.sphere.material = this.initMaterial(this.materialOptions); // 创建新材质
      this.sphere.material.needsUpdate = true; // 确保材质更新
    }
  }

  // 更新材质选项
  updateMaterialOptions(newOptions) {
    this.materialOptions = { ...this.materialOptions, ...newOptions };
    this.updateMaterial();
  }

  // 更新材质类型
  updateMaterialType(newType) {
    this.materialType = newType;
    this.updateMaterial();
  }

  // 处理窗口大小调整
  onWindowResize() {
    this.camera.aspect = this.dom.clientWidth / this.dom.clientHeight; // 更新相机的长宽比
    this.camera.updateProjectionMatrix(); // 更新相机的投影矩阵
    this.renderer.setSize(this.dom.clientWidth, this.dom.clientHeight); // 更新渲染器的大小
  }

  // 创建网格地面
  createGridFloor() {
    const size = 20; // 网格的尺寸
    const divisions = 20; // 网格的分割数
    const gridHelper = new THREE.GridHelper(size, divisions);
    this.scene.add(gridHelper);
  }

  // 更新纹理贴图
  updateTextureMap(file, key) {
    if (this.sphere) {
      const newMap = this.setMap(file); // 设置新的纹理
      this.sphere.material[key] = newMap; // 更新材质的对应属性
      this.sphere.material.needsUpdate = true; // 确保材质更新
    } else {
      console.error("球体尚未初始化。");
    }
  }

  // 设置材质颜色
  setColor(hexColor) {
    if (this.material) {
      this.material.color.set(hexColor); // 设置材质颜色
    }
  }

  // 设置材质属性
  setProperty(property, value) {
    if (this.material) {
      this.material[property] = value; // 设置材质属性
      this.material.needsUpdate = true; // 确保材质更新
    }
  }

  // 获取所有材质属性
  getAllProperties() {
    return this.material; // 获取材质所有属性
  }

  // 动画渲染循环
  animate() {
    requestAnimationFrame(() => this.animate()); // 请求下一帧动画
    this.renderer.render(this.scene, this.camera); // 渲染场景
  }
}

export default AdvancedMaterialSphere;

3.使用实例

import * as THREE from "three";
import AdvancedMaterialSphere from "./AdvancedMaterialSphere.js";

// 获取容器元素
const container = document.getElementById('container');

// 定义材质选项
const materialOptions = {
  color: 0x0077ff, // 蓝色
  roughness: 0.5, // 粗糙度
  metalness: 0.5, // 金属度
  normalMap: '/path/to/normalMap.jpg', // 正常贴图路径
  roughnessMap: '/path/to/roughnessMap.jpg', // 粗糙度贴图路径
};

// 创建AdvancedMaterialSphere实例
const sphere = new AdvancedMaterialSphere(container, materialOptions, "MeshStandardMaterial");

// 示例:更新材质选项
setTimeout(() => {
  sphere.updateMaterialOptions({
    color: 0xff0000, // 红色
  });
}, 3000);

// 示例:更新材质类型
setTimeout(() => {
  sphere.updateMaterialType("MeshPhongMaterial");
}, 6000);

// 示例:更新纹理贴图
setTimeout(() => {
  sphere.updateTextureMap('/path/to/newTexture.jpg', 'map');
}, 9000);

// 示例:设置材质颜色
setTimeout(() => {
  sphere.setColor(0x00ff00); // 绿色
}, 12000);

 

详细说明

  • container: 容器元素的 DOM 引用,用于初始化 WebGLRenderer 并将其附加到 DOM。
  • materialOptions: 一个包含材质属性的对象,可用于设置颜色、粗糙度、金属度等属性。
  • materialType: 材质类型的字符串值,例如 "MeshStandardMaterial""MeshPhongMaterial" 等。

动态更新

AdvancedMaterialSphere 类支持动态更新材质属性、材质类型和纹理贴图。你可以使用以下方法进行更新:

  • updateMaterialOptions(newOptions): 更新材质选项。
  • updateMaterialType(newType): 更新材质类型。
  • updateTextureMap(file, key): 更新材质的纹理贴图。
  • setColor(hexColor): 设置材质颜色。

示例代码解释

  • 创建实例: 创建 AdvancedMaterialSphere 实例时,传递容器元素、材质选项和材质类型。
  • 动态更新: 使用 setTimeout 演示如何在不同时间点动态更新材质选项、类型和贴图。

通过以上步骤,你应该能够成功创建并使用 AdvancedMaterialSphere 类,渲染一个可定制的材质球体,并能够动态更新其属性。

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

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

相关文章

每日复盘-20240527

今日关注: 六日涨幅最大: ------1--------300956--------- 英力股份 五日涨幅最大: ------1--------300956--------- 英力股份 四日涨幅最大: ------1--------301361--------- 众智科技 三日涨幅最大: ------1--------301361--------- 众智科技 二日涨幅最大: ----…

PGP安装以及汉化

目录 1.安装 2.汉化 1.安装 (1)进入setup目录,双击安装包开始安装 (2)选择默认语言English (3)接受安装协议 I accept the license agreement (4)选择第二项 Do not display the Release Notes (5)选择“…

解决 WooCommerce 的分析报表失效问题

今天明月的一个境外电商客户反应网站的 WooCommerce 分析报表已经十多天没有更新了,明明每天都有订单交易可分析报表里的数据依旧是十多天前的,好像更新完全停滞了似的。明月也及时的查看了后台的所有设置,确认没有任何问题,WooCo…

内存泄漏案例分享3-view的内存泄漏

案例3——view内存泄漏 前文提到,profile#Leaks视图无法展示非Activity、非Fragment的内存泄漏,换言之,除了Activity、Fragment的内存泄漏外,其他类的内存问题我们只能自己检索hprof文件查询了。 下面有一个极佳的view内存泄漏例子…

安装HAP时提示“code:9568344 error: install parse profile prop check error”错误

问题现象 在启动调试或运行应用/服务时,安装HAP出现错误,提示“error: install parse profile prop check error”错误信息。 解决措施 该问题可能是由于应用使用了应用特权,但应用的签名文件发生变化后未将新的签名指纹重新配置到设备的特…

shell编程之面交互

Here Document Here Document使用注意事项 面交互 面交互修改账号密码 [rootlocalhost opt]# passwd zhangsan <<EOF > abc1234 #下面两行是输入密码 > abc1234 > EOF 更改用户 zhangsan 的密码 。 新的 密码&#xff1a;无效的密码&#xff1a;…

GBase 8s 如何查看回滚的事务 和对应的SQL

描述&#xff1a; 如何查看当前数据库中是否有事务在回滚&#xff0c; 如果有&#xff0c; 具体是哪条 SQL 在回滚&#xff1f; 解决办法&#xff1a; 方法1&#xff1a; 通过 onstat -u|grep RP&#xff1b; 可以获取相关的 sessionid。 通过 onstat -g ses sid 获取 SQL&a…

红外成像人员检测数据集VOC+YOLO格式5838张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;5838 标注数量(xml文件个数)&#xff1a;5838 标注数量(txt文件个数)&#xff1a;5838 标注…

7个靠谱的副业赚钱方法,个个都可以月入过万!宝妈,上班族,学生党都可以做的兼职副业

你是不是也有过这样的困扰&#xff0c;生活费不够用&#xff0c;想要找个兼职贴补家用或者满足自己的小欲望&#xff1f;今天&#xff0c;我就带你一起走进这个五彩斑斓的兼职世界&#xff0c;让你轻松实现月入过千的小目标&#xff01; 在我多年的兼职探险历程中&#xff0c;我…

Leetcode 2028

思路&#xff1a;1-6之间的的n个数组合起来要变成sum_t mean*(rolls.size()n) - sum(rolls) ; 那么可以先假设每个数都是sum_t / n 其中这个数必须要在1 - 6 之间否者无法分配。 然后可以得出n * (sum_t / n ) < sum ; 需要对余数mod进行调整&#xff0c;为了减少调整的次…

【UE5.1 角色练习】06-角色发射火球-part1

前言 在上一篇&#xff08;【UE5.1 角色练习】05-火球发射物-CSDN博客&#xff09;基础上实现角色可以发射火球的技能 效果 步骤 一、准备 1. 打开角色蓝图&#xff0c;添加两个浮点型变量&#xff0c;分别表示当前的MP值和满状态的MP值 添加一个函数&#xff0c;这里命名…

网络原理-HTTP协议

HTTP协议 HTTP协议全称为超文本传输协议,除了能传输字符串,还能传输图片、视频、音频等。 当我们在访问网页的时候,浏览器会从服务器上下载数据,这些数据都会放在HTTP响应中,然后浏览器再根据这个HTTP响应显示出网页信息。 抓包 抓包工具本质上是一个代理工具,即我们将构造…

30.哀家要长脑子了!---栈与队列

1.388. 文件的最长绝对路径 - 力扣&#xff08;LeetCode&#xff09; 其实看懂了就还好 用一个栈来保存所遍历过最大的文件的绝对路径的长度&#xff0c;栈顶元素是文件的长度&#xff0c;栈中元素的个数是该文件目录的深度&#xff0c;非栈顶元素就是当时目录的长度 检查此…

安卓获取内部存储信息

目录 前言获取存储容量 前言 原生系统设置里的存储容量到底是怎么计算的&#xff0c;跟踪源码&#xff0c;涉及到VolumeInfo、StorageManagerVolumeProvider、PrivateStorageInfo、StorageStatsManager......等等&#xff0c;java上层没有办法使用简单的api获取到吗&#xff1f…

揭秘黄金分割数列:斐波那契数列的奇妙之旅

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、黄金分割数列——斐波那契数列的简介 二、实现斐波那契数列的函数 三、斐波那契数列在…

基于Cortex的MCU设计

基于Cortex的MCU设计 今日更新的存货文档&#xff0c;发现日更文章还是很花时间的。保证一周更新三篇文章就行啦&#xff0c;本篇文章的内容起始主要取自于《Cortex-M3 权威指南》和知网下载的论文。写的不详细&#xff0c;想进一步了解的就去看这篇文档或网上找别的资料&#…

FreeRtos进阶——关于任务的深入探究

创建任务函数 在我们创建任务中&#xff0c;会有几个比较神奇的参数&#xff0c;例如函数名称&#xff0c;以及栈大小。在我们创建任务时&#xff0c;也相应的要为每一个任务创建栈。这里面的栈除了用于任务数组开辟的空间外&#xff0c;还可以用于保存现场&#xff0c;例如有S…

kubernetes(k8s) v1.30.1 创建本地镜像仓库 使用本地docker镜像仓库部署服务 Discuz X3.5 容器搭建论坛

1 master11创建本地镜像仓库 [rootmaster11 ~]# docker run -d -p 5000:5000 --restartalways --name registry registry:2 Unable to find image registry:2 locally 2: Pulling from library/registry 79e9f2f55bf5: Pull complete 0d96da54f60b: Pull complete 5b27040df…

5月27日

思维导图 #include <iostream>using namespace std; namespace st_open {string a1;string retval(string a1);} using namespace st_open; int main() {getline(cin,a1);cout << "逆置前的字符串&#xff1a;" << a1 << endl;a1rerval(a1);…

YOLOV8逐步分解(5)_模型训练初始设置之混合精度训练AMP

yolov8逐步分解(1)--默认参数&超参配置文件加载 yolov8逐步分解(2)_DetectionTrainer类初始化过程 yolov8逐步分解(3)_trainer训练之模型加载_yolov8 加载模型-CSDN博客 YOLOV8逐步分解(4)_模型的构建过程 在上述文章逐步分解&#xff08;3&#xff09;和&#xff08;4&…