threeJs+vue 轻松切换几何体贴图

news/2025/2/27 11:10:13

        嗨,我是小路。今天主要和大家分享的主题是“threeJs+vue 轻松切换几何体贴图”。        

        想象一下,手头上正好有个在线3D家具商店,用户不仅可以看到产品的静态图片,还能实时更换沙发的颜色或材质,获得真实的购物体验。这就是我们今天分享的主题——通过简单的几行代码,您可以赋予任何3D几何体不同的外观,增强用户的参与感。

几何体贴图示例

一、示例介绍

设计一个几何体,并通过gui切换3d几何体上不同的图片。就像LOL切换皮肤,只是这个是最基础的。

1.加载图片

定义:通过纹理贴图加载器,将图片加载到几何体的材质里面。主要是放入.map属性里面。

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

2.图片偏移+动画

定义:将图片距离初始位置,平移一定的距离。

//纹理U方向偏移 将图片进行偏移
texture.offset.x += 0.5;
//渲染
const render = () => {
//贴图动画,每次渲染时,动画平移0.01
  texture?texture.offset.x += 0.01:null;
  renderer.render(scene, camera); //执行渲染操作
  meshObjx.bool ? mesh.rotateX(0.01) : null;//每次绕y轴旋转0.01弧度
  meshObjy.bool ? mesh.rotateY(0.01) : null;//每次绕y轴旋转0.01弧度
  meshObjz.bool ? mesh.rotateZ(0.01) : null;//每次绕y轴旋转0.01弧度
  //重复渲染
  requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}

3.图片显示面

定义:一般几何体默认有两个面,一个是正面,一个是背面。当图片切换成矩形平面时,这种效果更为明显。具体的效果,可看详情图。

child.material.side = THREE.SingleSide

二、实例代码

1、封装代码

const imgArr = reactive({ img: "" })
let texture = reactive("");
//更换模型图片
const setImg = () => {
  //.load()方法加载图像,返回一个纹理对象Texture
  const girlTexture = texLoader.load('./girl.png');
  const goalTexture = texLoader.load('./goal.png');

  //切换模型上图片
  gui.add(imgArr, 'img', { '可爱': 1, '目标': 2 }).name('模型图片').onChange((value) => {
    //遍历mesh下所有的对象
    mesh.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        if ([1, 2].includes(value)) {
          
          switch (value) {
            case 1:
              texture = girlTexture
              break;
            case 2:
              texture = goalTexture
              break;
            default:
              break;
          }
          //给模型进行贴图
          // 设置阵列模式
          texture.wrapS = THREE.RepeatWrapping;
          texture.wrapT = THREE.RepeatWrapping;

          // uv两个方向纹理重复数量 2*2 = 4个图片 自动将图片按照制定的数量平铺
          texture.repeat.set(2, 2);//注意选择合适的阵列数量
          //纹理U方向偏移 将图片进行偏移
          texture.offset.x += 0.5;


          child.material.map = texture;
          //THREE.SingleSide 单面,THREE.DoubleSide 双面  默认是双面贴图
          console.log(THREE.DoubleSide)
          child.material.side = THREE.SingleSide
          //开启透明
          child.material.transparent = false

          //更新模型
          child.material.needsUpdate = true;
        }
      }
    })
  })
}

2、整个示例代码

<template>
  <div class="pageBox">
    <div class="leftBox" ref="leftRef"></div>
    <div class="rightBox" ref="rightRef" :style="{ background: bgColor }"></div>
  </div>

</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import * as THREE from 'three';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 设置相机控件轨道控制器OrbitControls

// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
const bgColor = ref("")
// 实例化一个gui对象
const gui = new GUI();

const leftRef = ref();
const rightRef = ref()
const meshObjx = reactive({ bool: false })
const meshObjy = reactive({ bool: false })
const meshObjz = reactive({ bool: false })
// 定义相机输出画布的尺寸(单位:像素px)
let width = 800; //宽度
let height = window.innerHeight; //高度
// 创建3D场景对象Scene
const scene = new THREE.Scene();

// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera(50, width / height, 2, 6000);

//创建一个平面矩形对象Geometry
const geometry = new THREE.PlaneGeometry(150, 150, 150);

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

//创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
  color: 0xffffff,//0xff0000设置材质颜色为红色
  opacity: 0.5,
});
//将集合形体和材质融合
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh




// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();

onMounted(() => {

  initData()
  render();

  //添加相机空间
  const controls = new OrbitControls(camera, renderer.domElement);
  // 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
  controls.addEventListener('change', function () {
    renderer.render(scene, camera); //执行渲染操作
  });//监听鼠标、键盘事件

  //当窗口发生改变时,触发时间,重新渲染
  window.onresize = () => {
    height = window.innerHeight;
    width = window.innerWidth / 2;
    renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
    initData()
  }
})
const initData = () => {
  //设置网格模型在三维空间中的位置坐标,默认是坐标原点
  mesh.position.set(0, 10, 0);
  scene.add(mesh);
  //相机在Three.js三维坐标系中的位置
  // 根据需要设置相机位置具体值
  camera.position.set(200, 200, 200);
  camera.lookAt(mesh.position);//指向mesh对应的位置
  // AxesHelper:辅助观察的坐标系
  const axesHelper = new THREE.AxesHelper(150);
  scene.add(axesHelper);

  // 添加一个辅助网格地面
  // const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);
  // scene.add(gridHelper);

  setGui();


  renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
  //将innerHTML置空,避免append重复添加渲染
  leftRef.value.innerHTML = ''
  leftRef.value.append(renderer.domElement);
}

//设置gui
const setGui = () => {
  //改变交互界面style属性
  gui.domElement.style.right = '0px';
  gui.domElement.style.width = '300px';
  //将gui和 几何体的位置绑定
  //将对应的属性x重命名
  gui.add(mesh.position, 'x', 0, 100).name('几何体x轴').onChange((value) => {
    console.log('x', value)
    //改变x时,进行其它的操作
    mesh.position.y = 50
  })
  //设置交互界面每次改变属性值间隔是多少
  gui.add(mesh.position, 'y', 0, 100).step(0.1)
  //设置交互界面 生成交互界面是下拉菜单,设置为数组
  // gui.add(mesh.position,'z',[-100,0,100]).name("Z轴")
  gui.add(mesh.position, 'z', { 'left': -100, 'center': 0, 'right': 100 }).name("Z轴")

  //设置交互页面 单选框  当变量的类型为布尔类型时,才会生成单选框
  gui.add(meshObjx, 'bool').name("是否开启x轴旋转")
  gui.add(meshObjy, 'bool').name("是否开启y轴旋转")
  gui.add(meshObjz, 'bool').name("是否开启z轴旋转")

  //改变颜色
  gui.addColor({ color: 0x00ffff }, 'color').onChange((value) => {
    //value是十进制,需要转换成十六进制
    bgColor.value = '#' + value.toString(16);
    mesh.material.color.set(value)
  })

  setImg();
}

const imgArr = reactive({ img: "" })
let texture = reactive("");
//更换模型图片
const setImg = () => {
  //.load()方法加载图像,返回一个纹理对象Texture
  const girlTexture = texLoader.load('./girl.png');
  const goalTexture = texLoader.load('./goal.png');

  //切换模型上图片
  gui.add(imgArr, 'img', { '可爱': 1, '目标': 2 }).name('模型图片').onChange((value) => {
    //遍历mesh下所有的对象
    mesh.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        if ([1, 2].includes(value)) {
          
          switch (value) {
            case 1:
              texture = girlTexture
              break;
            case 2:
              texture = goalTexture
              break;
            default:
              break;
          }
          //给模型进行贴图
          // 设置阵列模式
          texture.wrapS = THREE.RepeatWrapping;
          texture.wrapT = THREE.RepeatWrapping;

          // uv两个方向纹理重复数量 2*2 = 4个图片 自动将图片按照制定的数量平铺
          texture.repeat.set(2, 2);//注意选择合适的阵列数量
          //纹理U方向偏移 将图片进行偏移
          texture.offset.x += 0.5;


          child.material.map = texture;
          //THREE.SingleSide 单面,THREE.DoubleSide 双面  默认是双面贴图
          console.log(THREE.DoubleSide)
          child.material.side = THREE.SingleSide
          //开启透明
          child.material.transparent = false

          //更新模型
          child.material.needsUpdate = true;
        }
      }
    })
  })
}
//渲染
const render = () => {
  texture?texture.offset.x += 0.01:null;
  renderer.render(scene, camera); //执行渲染操作
  meshObjx.bool ? mesh.rotateX(0.01) : null;//每次绕y轴旋转0.01弧度
  meshObjy.bool ? mesh.rotateY(0.01) : null;//每次绕y轴旋转0.01弧度
  meshObjz.bool ? mesh.rotateZ(0.01) : null;//每次绕y轴旋转0.01弧度
  //重复渲染
  requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}

</script>
<style scoped lang="less">
.pageBox {
  width: 100%;
  height: 100vh;
  padding: 0;
  margin: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;

  .rightBox {
    width: 100%;
    height: 100%;
    background: yellow;
  }
}
</style>

三、注意事项

       1、在vue框架的基础上,一定要注意加载图片存放的路径。默认的当前文件夹是.public,不是src。

        2、一定要设置模型的.needUpdate 为true,重新对3d几何体进行渲染;否则容易出现gui切换了图片,但是3d几何体上图片未做任何的切换的问题。

如果你认为该文章对你有帮助,伸出您的小手,帮忙【点赞】+【关注】+【收藏】。


http://www.niftyadmin.cn/n/5870016.html

相关文章

模型和数据集的平台之在Hugging Face上进行模型下载、上传以及创建专属Space

模型下载 步骤&#xff1a; 注册Hugging Face平台 https://huggingface.co/ 新建一个hf_download_josn.py 文件 touch hf_download_josn.py 编写hf_download_josn.py文件 import os from huggingface_hub import hf_hub_download# 指定模型标识符 repo_id "inter…

腾讯云 Elasticsearch Service:一站式云端搜索与分析解决方案

在数据驱动的时代&#xff0c;企业面临着海量数据存储、检索、分析等复杂挑战。无论是电商、金融、SaaS、物联网&#xff0c;还是日志管理、安全监测、用户行为分析&#xff0c;高效的数据查询与分析能力已成为提升业务竞争力的关键。 Elasticsearch 作为一款开源的分布式搜索…

计算机视觉|Mask2Former:开启实例分割新范式

一、图像分割&#xff1a;技术演进与挑战 在计算机视觉领域&#xff0c;图像分割是一项至关重要的任务&#xff0c;它就像是为计算机赋予了一双能够理解图像内容的 “慧眼”。简单来说&#xff0c;图像分割的目的是将图像中的不同物体或区域进行划分&#xff0c;让计算机能够识…

排查和解决线程池瓶颈问题案例

在分布式系统中&#xff0c;线程池的使用非常普遍&#xff0c;尤其是在处理异步任务时。然而&#xff0c;线程池的配置不当可能会导致性能瓶颈&#xff0c;进而影响系统的整体性能。本文将分享一个实际案例&#xff0c;介绍如何通过日志分析和线程池优化来解决系统中的性能瓶颈…

跟着 Lua 5.1 官方参考文档学习 Lua (9)

文章目录 5 – Standard Libraries5.1 – Basic Functions错误处理error (message [, level])例子&#xff1a;error 函数的使用例子&#xff1a;error 函数 level 参数例子&#xff1a;使用 pcall 函数捕获错误 assert (v [, message])例子&#xff1a;assert 函数的使用 chun…

电脑键盘知识

1、键盘四大功能区 1. 功能区 2. 主要信息输入区 3. 编辑区 4. 数字键盘区 笔记本电脑键盘的功能区&#xff0c;使用前需先按Fn键 1.1、功能区 ESC&#xff1a;退出 F1&#xff1a;显示帮助信息 F2&#xff1a;重命名 F4&#xff1a;重复上一步操作 F5&#xff1a;刷新网页 …

python 程序

gif调整尺寸.py import sys from PIL import Image,ImageSequence import os ##print(sys.argv[0]) ##print(sys.argv[1]) def gifresize(file_name): gf Image.open(file_name) ## lifetime gf.info[duration] imglist [] imgnew [] for i in ImageSequence.…

AI工具箱最新使用教程

先克隆项目 电脑需要先安装 git &#xff0c;安装的画看这个 Git安装教程&#xff08;超详细&#xff09;。 git镜像 git clone https://github.com/Escaflowne1985/MyToolsWebBackendUser.gitgitee镜像 git clone https://gitee.com/escaflowne/MyToolsWebBackendUser.git…