上节学了贴花几何体,可以给网格模型表面加一些装饰:

这节我们来实现 T 恤印花设计的功能:

我们要做上传图片、切换颜色、切换图片等交互,这里需要用前端框架。
所以我们是 React + Three.js 来写,当然,你用 vue 也可以,都差不多。
创建项目:
npx create-vite t-shirt-design

选择 react、js 来创建 vite 项目。
进入项目,安装依赖:
pnpm install
pnpm install --save three
pnpm install --save-dev @types/three
去掉 StrictMode 和 index.css

然后改一下 App.jsx
import { useEffect, useRef, useState } from 'react';
import { init } from './3d-init'
import './App.css'
function App() {
useEffect(() => {
const dom = document.getElementById('content');
const { scene } = init(dom);
return () => {
dom.innerHTML = '';
}
}, []);
return <div>
<div id="main">
<div id="content">
</div>
</div>
</div>
}
export default App
在 App.css 写下样式:
body {
margin: 0;
}
然后来初始化 3d 场景:
创建 3d-init.js
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh';
export function init(dom) {
const scene = new THREE.Scene();
scene.add(mesh);
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(500, 400, 300);
scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0xffffff);
scene.add(ambientLight);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
camera.position.set(0, 500, 500);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(width, height);
function render(time) {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
dom.append(renderer.domElement);
window.onresize = function () {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
};
const controls = new OrbitControls(camera, renderer.domElement);
return {
scene,
renderer,
controls
}
}
创建 mesh.js
import * as THREE from 'three';
const group = new THREE.Group();
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshLambertMaterial({
color: 'orange'
});
const mesh = new THREE.Mesh(geometry, material);
group.add(mesh);
export default group;
我们先跑起来看下:
npm run dev


没啥问题。
然后我们找个T恤模型:
你可以从这里下载:
https://github.com/QuarkGluonPlasma/threejs-course-code/blob/main/t-shirt-design/public/tshirt.glb

放到 public 目录下:

在 mesh.js 加载下:
import * as THREE from 'three';
import { DRACOLoader, GLTFLoader } from 'three/examples/jsm/Addons.js';
const group = new THREE.Group();
const gltfLoader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/' );
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load('./tshirt.glb', (gltf) => {
group.add(gltf.scene);
gltf.scene.scale.setScalar(1000);
});
export default group;
这个模型做了 draco 压缩,所以我们要用 DracoLoader 来解压。
看下效果:

没啥问题。
我们要在白色T恤上印花,那得知道位置。
这个用 RayCaster 在点击的时候获取就行。
先准备下图片:


然后加一下点击事件:

const loader = new THREE.TextureLoader();
const texture = loader.load('./heart.png');
texture.colorSpace = THREE.SRGBColorSpace;
renderer.domElement.addEventListener('click', (e) => {
const y = -((e.offsetY / height) * 2 - 1);
const x = (e.offsetX / width) * 2 - 1;
const rayCaster = new THREE.Raycaster();
rayCaster.setFromCamera(new THREE.Vector2(x, y), camera);
const intersections = rayCaster.intersectObjects(mesh.children);
if(intersections.length) {
const position = intersections[0].point;
const orientation = new THREE.Euler();
const size = new THREE.Vector3(100, 100, 100);
const geometry = new DecalGeometry(intersections[0].object, position, orientation, size);
const material = new THREE.MeshPhongMaterial({
polygonOffset: true,
polygonOffsetFactor: -4,
map: texture,
transparent: true,
});
const mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);
}
});
点击的时候,拿到交点位置 intersections[0].point,在那个位置创建一个贴花几何体 DecalGeometry,设置颜色贴图为 heart.png
看下效果:

这样,就可以在T恤上印花了。
案例代码上传了小册仓库。
总结
这节我们实现了T恤印花的功能。
首先,创建了 react + three.js 的项目,因为后面会有一些前端的表单交互。
加载了 draco 压缩的T恤模型,处理点击事件,在交点位置添加 DecalGeometry 实现印花功能。
下节我们来加上更多图案的选择,以及用户自己上传图案的功能。