Skip to content

77. 贴花几何体:给物体加一些装饰

Published:

这节我们来学一个新的几何体:DecalGeometry 贴花几何体

image.png

它可以给目标物体增加一些装饰。

什么意思呢?

比如说 T 恤的模型上,你想增加一些特殊的图案:

2025-06-06 21.06.44.gif

但这个模型本身是凹凸不平的,如何紧贴着表面给他加一些东西呢?

或者说篮球的签名:

image.png

如果你有一个篮球模型,如何给他动态添加一些签名的装饰呢?

这种就要用到贴花几何体了。

它就是用来创建贴在一个网格模型表面的装饰。

我们来试一下:

npx create-vite decal-geometry

image.png

进入项目,安装依赖:

npm install
npm install --save three
npm install --save-dev @types/three

改下 src/main.js

import './style.css';
import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';

const scene = new THREE.Scene();
scene.add(mesh);

const directionLight = new THREE.DirectionalLight(0xffffff, 2);
directionLight.position.set(500, 400, 300);
scene.add(directionLight);

const ambientLight = new THREE.AmbientLight();
scene.add(ambientLight);

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
camera.position.set(500, 600, 400);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({
    antialias: true
});
renderer.setSize(width, height)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

创建 Scene、Light、Camera、Renderer。

改下 style.css

body {
  margin: 0;
}

然后在 mesh.js 创建一个球:

import * as THREE from 'three';

const group = new THREE.Group();

const geometry = new THREE.SphereGeometry(200);
const material = new THREE.MeshPhongMaterial({
    color: 'orange'
});
const mesh = new THREE.Mesh(geometry, material);
group.add(mesh);

export default group;

跑一下:

npm run dev

image.png

2025-06-06 21.20.32.gif

然后在上面加一个图片,紧贴表面。

创建贴花几何体 DecalGeometry:

image.png

DecalGeometry 是附在另一个网格模型表面,所以要传入 mesh 以及位置、角度、大小。

然后不能紧贴表面,让它有个 -4 的偏移。

const position = new THREE.Vector3(0, 0, 200);
const orientation = new THREE.Euler();
const size = new THREE.Vector3(100, 100, 100);
const geometry2 = new DecalGeometry(mesh, position, orientation, size);
const material2 = new THREE.MeshPhongMaterial({
    polygonOffset: true,
    polygonOffsetFactor: -4,
    color: 'green',
});
const mesh2 = new THREE.Mesh(geometry2, material2);
group.add(mesh2);

看下效果:

2025-06-06 21.27.55.gif

可以看到,现在球体表面就多了一个装饰,这个就是贴花几何体。

image.png

这俩参数是防止深度冲突的,去掉的话,贴花和表面重合就会闪烁,因为不知道哪个在上面:

2025-06-06 21.29.35.gif

然后我们找张图片试一下:

image.png

xiaoxin.png

image.png

const loader = new THREE.TextureLoader();
const texture = loader.load('./xiaoxin.png');
texture.colorSpace = THREE.SRGBColorSpace;
const material2 = new THREE.MeshPhongMaterial({
    polygonOffset: true,
    polygonOffsetFactor: -4,
    // color: 'green',
    map: texture,
    transparent: true
});

看下效果:

2025-06-06 21.59.20.gif

换个复杂一点的几何体:

image.png

const geometry = new THREE.DodecahedronGeometry(200);

换成正十二面体。

2025-06-06 22.00.56.gif

也同样是紧贴着几何体。

如果我要贴在某个角上,如何知道位置呢?

可以用 RayCaster 来实现:

我们添加一个点击事件:

image.png

const loader = new THREE.TextureLoader();
const texture = loader.load('./xiaoxin.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) {
      console.log(intersections);
      const position = intersections[0].point;
      console.log(position);

      const orientation = new THREE.Euler();
      const size = new THREE.Vector3(100, 100, 100);
      const geometry1 = new DecalGeometry(intersections[0].object, position, orientation, size);
      const material1 = new THREE.MeshPhongMaterial({
          polygonOffset: true,
          polygonOffsetFactor: -4,
          map: texture,
          transparent: true,
      });
      const mesh1 = new THREE.Mesh( geometry1, material1 );
      scene.add(mesh1);
    }
});

点击选中 3D 场景的物体后,我们通过 object 拿到点击的物体,给他添加贴花几何体。

贴花几何体的位置从 intersections[0].point 来取。

2025-06-06 22.08.00.gif

你可以把调好的位置记下来,更新到代码里:

image.png

image.png

这样,最开始就是展示在目标位置了:

2025-06-06 22.10.51.gif

案例代码上传了小册仓库

总结

这节我们学了贴花结合体 DecalGeometry,它可以给物体表面增加一些装饰。

比如 T 恤添加一些图案、篮球做签名等,都可以用贴花几何体来做。

但在什么位置添加贴花不好计算,可以直接用 RayCaster 处理点击,取 intersections[0].point 就行。

贴花几何体比较特殊,在需要给物体添加装饰的时候很有用。

评论