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

发布于:2024-05-22 ⋅ 阅读:(165) ⋅ 点赞:(0)

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

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 MaterialSphere {
  constructor(
    dom,
    materialOptions = {},
    materialValue = "MeshStandardMaterial"
  ) {
    this.dom = dom; // 保存DOM引用
    this.materialOptions = materialOptions; // 保存初始材质选项
    this.materialValue = materialValue; // 保存初始材质值

    // 创建场景
    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元素添加到文档中
    dom.appendChild(this.renderer.domElement);

    // 光探测器
    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);

    // 纹理贴图加载器TextureLoader
    this.texLoader = new THREE.TextureLoader();
    // 环境贴图
    this.scene.background = new THREE.CubeTextureLoader()
      .setPath("/pisa/")
      .load(
        ["px.png", "nx.png", "py.png", "ny.png", "pz.png", "nz.png"],
        (cubeTexture) => {
          this.scene.background = cubeTexture; // 设置场景背景为加载的立方体贴图
          this.lightProbe.copy(
            LightProbeGenerator.fromCubeTexture(cubeTexture)
          ); // 从立方体贴图生成光探测器
          const geometry = new THREE.SphereGeometry(1, 32, 32);
          // 创建球体网格并加入场景
          this.sphere = new THREE.Mesh(
            geometry,
            this.initMaterial(materialOptions)
          );
          // 设置球体位置
          this.sphere.position.set(0, 0, 0);
          // 将球体加入场景
          this.scene.add(this.sphere);
        },
        () => {
          console.log("将在加载过程中进行调用");
        },
        (err) => {
          console.log(err);
          console.log("加载错误时被调用");
        }
      );

    // 设置相机控件轨道控制器OrbitControls
    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);
    });

    // // 创建网格地面
    // this.createGridFloor()

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

    // 立即调用一次以适应初始大小
    this.onWindowResize(dom);
    // 开始渲染循环
    this.animate();
  }

  initMaterial(mat) {
    // 创建球体几何体
    let { map, ...Options } = mat;
    // 创建材质
    this.material = new THREE[this.materialValue]({
      envMapIntensity: 0.5, // 设置环境贴图强度
      map: this.setMap(map),
      ...Options,
    });
    return this.material;
  }
  //更新材质属性
  updateMaterial(value) {
    this.materialValue = value;
    this.sphere.material = this.initMaterial(this.materialOptions);
  }

  // 更新材质
  updateMaterialOptions(newOptions) {
    this.materialOptions = { ...this.materialOptions, ...newOptions };
    this.sphere.material = this.initMaterial(this.materialOptions);
  }

  // 动态调整渲染器尺寸和相机长宽比的方法
  onWindowResize(dom) {
    // 更新相机的长宽比
    this.camera.aspect = dom.clientWidth / dom.clientHeight;
    // 更新相机的投影矩阵(这是必须的)
    this.camera.updateProjectionMatrix();

    // 更新渲染器的大小
    this.renderer.setSize(dom.clientWidth, dom.clientHeight);
  }

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

  // 设置纹理
  setMap(file) {
    // .load()方法加载图像,返回一个纹理对象Texture
    if (!file) return "";
    const texture = this.texLoader.load(file);
    return texture;
  }

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

  //设置属性
  setProperty(property, value) {
    this.material[property] = value;
  }

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

  // 动画渲染方法
  animate() {
    requestAnimationFrame(() => this.animate());
    this.renderer.render(this.scene, this.camera);
  }
}

export default MaterialSphere;

//使用
//获取dom
// const container = document.getElementById("modeleStateContent");
// //初始化
//传入初始化材质和dom
// materialSphere = new MaterialSphere(container, initialMaterial);
//单独设定属性
// materialSphere.setProperty;
//获取所有属性
// materialSphere.getAllProperty();、
//更换所有属性
// materialSphere.updateMaterialOptions({
//   color: 0xffffff, // 设置材质颜色
//   roughness: 0, // 设置粗糙度
//   metalness: 0.5, // 设置金属度
//   opacity: 1, // 设置材质透明度
//   transparent: true, // 设置材质是否透明
//   side: THREE.DoubleSide, // 设置材质的两面
//   depthTest: true, // 设置深度检测
//   depthWrite: true, // 设置深度写入
//   map: "/textures/earth.jpg",
// });