joken-前端工程师

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::
  404 随笔 :: 39 文章 :: 8 评论 :: 20万 阅读

目标

  • 点击模型的某个区域时,弹出图片选择框。
  • 用户选择新图片后,替换该区域的纹理。

实现步骤

1. 创建基础场景

设置 Three.js 的场景、相机、渲染器和光源。

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 添加光源
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 10, 10).normalize();
scene.add(light);

// 动画循环
function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}
animate();

2. 加载模型并分配初始材质

使用 GLTFLoader 加载模型,并为每个区域分配初始材质。

let model;
const loader = new GLTFLoader();
loader.load('path/to/your-model.glb', (gltf) => {
    model = gltf.scene;
    scene.add(model);

    // 遍历模型子对象并分配初始材质
    model.traverse((child) => {
        if (child instanceof THREE.Mesh) {
            const textureLoader = new THREE.TextureLoader();
            const initialTexture = textureLoader.load('path/to/default-texture.jpg');
            child.material = new THREE.MeshStandardMaterial({ map: initialTexture });
            child.userData.originalMaterial = child.material; // 保存原始材质
            child.userData.region = child.name || 'Unknown'; // 设置区域标识
        }
    });
});

3. 实现点击检测

使用 THREE.Raycaster 检测鼠标点击的模型区域。

// 创建 Raycaster 和鼠标位置变量
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// 监听鼠标点击事件
window.addEventListener('click', (event) => {
    // 将鼠标位置归一化到 -1 到 1 的范围
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    // 更新射线投射器
    raycaster.setFromCamera(mouse, camera);

    // 检测与模型的交点
    const intersects = raycaster.intersectObjects(scene.children, true);
    if (intersects.length > 0) {
        const clickedObject = intersects[0].object;

        // 确保点击的是模型的某个部分
        if (clickedObject instanceof THREE.Mesh) {
            openTextureSelector(clickedObject); // 弹出纹理选择框
        }
    }
});

4. 弹出图片选择框

创建一个简单的图片选择框,允许用户选择新的纹理图片。

function openTextureSelector(mesh) {
    const textureSelector = document.createElement('div');
    textureSelector.style.position = 'fixed';
    textureSelector.style.top = '50px';
    textureSelector.style.left = '50px';
    textureSelector.style.backgroundColor = 'white';
    textureSelector.style.padding = '10px';
    textureSelector.style.zIndex = '1000';

    const title = document.createElement('h3');
    title.textContent = `选择新纹理(当前区域:${mesh.userData.region}`;
    textureSelector.appendChild(title);

    const imageList = ['texture1.jpg', 'texture2.jpg', 'texture3.jpg']; // 替换为你的纹理图片路径
    imageList.forEach((imagePath) => {
        const img = document.createElement('img');
        img.src = imagePath;
        img.style.width = '100px';
        img.style.height = '100px';
        img.style.margin = '5px';
        img.style.cursor = 'pointer';

        img.onclick = () => {
            applyTexture(mesh, imagePath); // 应用选中的纹理
            document.body.removeChild(textureSelector); // 关闭选择框
        };

        textureSelector.appendChild(img);
    });

    document.body.appendChild(textureSelector);
}

5. 应用新纹理

编写函数将用户选择的图片作为纹理应用到模型的对应区域。

function applyTexture(mesh, texturePath) {
    const textureLoader = new THREE.TextureLoader();
    const newTexture = textureLoader.load(texturePath);

    // 更新材质的纹理
    mesh.material.map = newTexture;
    mesh.material.needsUpdate = true; // 确保材质更新
}

完整流程总结

  1. 加载模型
    使用 GLTFLoader 加载模型,并为每个区域分配初始材质。通过 userData.region 标识不同的区域。

  2. 点击检测
    使用 THREE.Raycaster 检测鼠标点击的模型区域。

  3. 弹出图片选择框
    当用户点击模型时,弹出一个图片选择框,显示可用的纹理图片。

  4. 应用新纹理
    用户选择图片后,将其作为纹理应用到对应的模型区域。


关键点解析

  1. 如何确定点击的模型和区域?

    • 使用 THREE.Raycaster 获取被点击的对象。
    • 通过 mesh.userData.regionmesh.name 来标识不同的区域。
  2. 如何应用新纹理?

    • 修改材质的 map 属性,并确保调用 material.needsUpdate = true
  3. 用户体验优化

    • 在图片选择框中显示当前区域名称。
    • 可以为图片选择框添加关闭按钮或限制图片数量。

posted on   joken1310  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示