Three.js 3D模型展示与交互实现详解

本文将详细解析一个基于Three.js的3D模型展示与交互系统的完整实现代码,该系统包含了模型加载、环境光照、用户交互、响应式设计等多个功能模块。

一、代码概述

这段代码实现了一个完整的3D产品展示系统,主要功能包括:

  1. 创建3D场景并设置正交相机

  2. 加载并显示3D模型

  3. 实现模型旋转交互(鼠标拖拽和惯性效果)

  4. 添加加载动画

  5. 实现响应式布局

  6. 自定义光标效果

二、核心代码解析

1. 场景初始化

这部分代码初始化了Three.js的核心组件:

const scene = new THREE.Scene();
const boxElement = document.querySelector(".productModel_box");
const width = boxElement.clientWidth;
const height = boxElement.clientHeight;

// 设置正交相机
const aspect = width / height;
const frustumSize = 10;
const camera = new THREE.OrthographicCamera(
    (frustumSize * aspect) / -2,
    (frustumSize * aspect) / 2,
    frustumSize / 2,
    frustumSize / -2,
    0.1,
    1000
);
camera.position.z = 7.5;

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.NoToneMapping;
boxElement.appendChild(renderer.domElement);
  • scene: 3D场景容器

  • camera: 使用正交投影相机(OrthographicCamera),适合产品展示

  • renderer: WebGL渲染器,开启抗锯齿和透明背景

2. 加载动画实现

function createLoadingCube() {
    const loadingScene = new THREE.Scene();
    const loadingCamera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
    loadingCamera.position.z = 5;
    const loadingRenderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    loadingRenderer.setClearAlpha(0);
    loadingRenderer.setSize(100, 100);
    document.getElementById("loading-cube").appendChild(loadingRenderer.domElement);

    const cube = new THREE.Mesh(
        new THREE.BoxGeometry(2, 2, 2),
        new THREE.MeshBasicMaterial({ color: 0xfe5000, wireframe: true })
    );
    loadingScene.add(cube);
    loadingScene.add(new THREE.AmbientLight(0xffffff, 1));

    function animateLoadingCube() {
        requestAnimationFrame(animateLoadingCube);
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
        loadingRenderer.render(loadingScene, loadingCamera);
    }

    animateLoadingCube();
    return function () {
        document.getElementById("loading-cube").removeChild(loadingRenderer.domElement);
    };
}

const cleanupLoadingCube = createLoadingCube();

这段代码创建了一个旋转的线框立方体作为加载动画:

  1. 创建独立的场景、相机和渲染器

  2. 添加一个橙色线框立方体

  3. 实现立方体旋转动画

  4. 返回清理函数,用于加载完成后移除动画

3. 环境光照设置

const pmremGenerator = new THREE.PMREMGenerator(renderer);
new THREE.RGBELoader().load(
    "https://cdn.shopify.com/s/files/1/0753/2393/2896/files/photo_studio_01_2k.hdr?v=1746778998",
    (hdrTexture) => {
        const envMap = pmremGenerator.fromEquirectangular(hdrTexture).texture;
        scene.environment = envMap;
        hdrTexture.dispose();
        pmremGenerator.dispose();
    }
);

使用HDR环境贴图创建逼真的光照效果:

  1. 使用PMREMGenerator预处理环境贴图

  2. 加载2K分辨率的HDR贴图

  3. 将处理后的环境贴图应用到场景

4. 模型加载与设置

const loader = new THREE.GLTFLoader();
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/libs/draco/");
loader.setDRACOLoader(dracoLoader);

let model = null;
let initialRotation = { x: 0, y: Math.PI };
let hasPlayedScrollAnimation = false;

loader.load(`{{ section.settings.modelLink }}`, (gltf) => {
    model = gltf.scene;
    model.scale.set(7.5, 7.5, 7.5);
    model.position.set(0, -3.5, 0);

    model.traverse((child) => {
        if (child.isMesh && child.material) {
            // 可调整材质属性
        }
    });

    scene.add(model);
    initialRotation = { x: model.rotation.x, y: model.rotation.y };

    setTimeout(() => rotateY180(), 500);
    setupScrollTriggerWithGSAP();

    gsap.to("#loading-container", {
        opacity: 0,
        duration: 0.5,
        onComplete: () => {
            document.getElementById("loading-container").style.display = "none";
            cleanupLoadingCube();
        },
    });
});

模型加载流程:

  1. 使用GLTFLoader和DRACOLoader加载压缩的GLTF模型

  2. 设置模型缩放和位置

  3. 遍历模型所有子元素,可调整材质属性

  4. 添加模型到场景

  5. 加载完成后隐藏加载动画

  6. 执行初始旋转动画(rotateY180)

5. 交互系统实现

let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };
let targetRotation = { x: 0, y: 0 };
let currentRotation = { x: 0, y: 0 };
const maxRotationX = Math.PI / 3;
const rotationSensitivity = 0.003;
let velocity = { x: 0, y: 0 };
const friction = 0.95;

// 鼠标移动处理
document.addEventListener("mousemove", (e) => {
    mouseX = e.clientX;
    mouseY = e.clientY;

    if (isInsideBox && !isOverReturnIcon) {
        updateCursorPosition(mouseX, mouseY);
        cursor.style.display = "flex";
        document.body.classList.remove("normal-cursor");
    } else {
        cursor.style.display = "none";
        document.body.classList.add("normal-cursor");
    }

    if (!isDragging || !model) return;

    const deltaMove = {
        x: e.clientX - previousMousePosition.x,
        y: e.clientY - previousMousePosition.y,
    };

    velocity = {
        x: -deltaMove.y * rotationSensitivity * 0.5,
        y: deltaMove.x * rotationSensitivity * 0.5,
    };

    targetRotation.y += deltaMove.x * rotationSensitivity;
    targetRotation.x += deltaMove.y * rotationSensitivity;
    targetRotation.x = Math.max(-maxRotationX, Math.min(maxRotationX, targetRotation.x));
    previousMousePosition = { x: e.clientX, y: e.clientY };
});

// 动画循环
function animate() {
    requestAnimationFrame(animate);
    if (!isDragging) {
        targetRotation.y += velocity.y;
        targetRotation.x += velocity.x;
        velocity.x *= friction;
        velocity.y *= friction;
    }

    currentRotation.x += (targetRotation.x - currentRotation.x) * 0.1;
    currentRotation.y += (targetRotation.y - currentRotation.y) * 0.1;

    if (model) {
        model.rotation.x = currentRotation.x;
        model.rotation.y = currentRotation.y;
    }

    renderer.render(scene, camera);
}

交互系统关键点:

  1. 鼠标拖拽实现模型旋转

  2. 添加旋转惯性效果(velocity和friction)

  3. 限制X轴旋转角度(maxRotationX)

  4. 平滑过渡效果(使用currentRotation和targetRotation插值)

  5. 自定义光标效果

三、功能扩展点

这段代码还可以进一步扩展:

  1. 模型材质自定义:在模型加载后的traverse回调中可以修改材质属性

  2. 滚动动画:setupScrollTriggerWithGSAP函数可以扩展为基于滚动的动画

  3. 多点触控:当前只支持单点触控,可以扩展为多点触控缩放

  4. 模型点击事件:添加模型特定部件的点击交互

四、总结

这个3D模型展示系统实现了:

  • 专业的加载流程(加载动画+进度提示)

  • 高质量的渲染效果(HDR环境光)

  • 流畅的交互体验(拖拽+惯性)

  • 响应式设计(窗口大小变化适配)

  • 移动端支持(触摸事件处理)

通过分析这段代码,我们可以学习到如何使用Three.js构建一个完整的产品3D展示系统,涵盖了从加载到交互的完整流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值