You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无需Raycaster在Three.js对象上放置Decal?

无需Raycaster手动放置Three.js Decal贴纸

你不需要Raycaster也能轻松在模型上固定Decal,核心是明确DecalGeometry的位置参数是世界空间坐标,同时给贴纸设置贴合目标表面的旋转角度。以下是具体实现方法和修改后的代码:

核心思路

  1. 确定贴纸在目标网格局部空间的位置(比如盒子的正面、顶部、侧面中心)
  2. 将局部坐标转换为世界空间坐标(适配网格的位移、旋转)
  3. 设置贴纸的旋转角度,让它完全贴合目标表面

修改后的代码示例

import * as THREE from "three";
import { DecalGeometry } from "three/examples/jsm/geometries/DecalGeometry";

// Renderer
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera();
camera.position.z = 10;

// 目标网格(带旋转,模拟复杂场景)
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({
  color: "lightblue",
  flatShading: true
});
const mesh = new THREE.Mesh(geometry, material);
mesh.rotation.y = Math.PI/4; // 给网格加个旋转,测试坐标转换
scene.add(mesh);

// 函数:创建并添加Decal
function createDecal(localPos, rotation, size, color) {
  // 把局部坐标转成世界坐标(适配网格的位移、旋转)
  const worldPos = mesh.localToWorld(localPos.clone());
  
  const decalGeometry = new DecalGeometry(
    mesh,
    worldPos,
    rotation,
    size
  );
  const decalMaterial = new THREE.MeshStandardMaterial({
    color,
    polygonOffset: true,
    polygonOffsetFactor: -4,
    transparent: true // 可选:如果用带透明的贴图需要开启
  });
  const decal = new THREE.Mesh(decalGeometry, decalMaterial);
  scene.add(decal);
}

// 放置多个Decal示例
// 1. 正面(局部z=1,贴合z轴方向)
createDecal(
  new THREE.Vector3(0, 0, 1),
  new THREE.Euler(0, 0, 0),
  new THREE.Vector3(1, 1, 1),
  "pink"
);

// 2. 顶部(局部y=1,旋转-90度贴合顶面)
createDecal(
  new THREE.Vector3(0, 1, 0),
  new THREE.Euler(-Math.PI/2, 0, 0),
  new THREE.Vector3(1, 1, 1),
  "yellow"
);

// 3. 右侧(局部x=1,旋转90度贴合右侧面)
createDecal(
  new THREE.Vector3(1, 0, 0),
  new THREE.Euler(0, Math.PI/2, 0),
  new THREE.Vector3(1, 1, 1),
  "cyan"
);

// Light
const dlight = new THREE.DirectionalLight();
dlight.position.z = 10;
scene.add(dlight);

// Resize
function resize() {
  const width = document.documentElement.clientWidth;
  const height = window.innerHeight;

  camera.aspect = width / height;
  camera.updateProjectionMatrix();

  renderer.setPixelRatio(Math.min(2, devicePixelRatio));
  renderer.setSize(width, height);
}

window.addEventListener("resize", resize);
resize();

// Tick
function tick(t) {
  requestAnimationFrame(tick);

  mesh.rotation.y = t * 0.001;

  renderer.render(scene, camera);
}

requestAnimationFrame(tick);

关键细节说明

  • mesh.localToWorld(localPos.clone()):必须用clone(),否则会修改原局部坐标向量
  • 旋转角度的设置:根据目标表面的法向量调整Euler角,比如顶面的法向量是y轴正方向,所以需要把贴纸绕x轴旋转-90度,让贴纸正面朝向y轴正方向
  • polygonOffset参数:避免贴纸和目标网格的Z轴冲突,防止闪烁

内容的提问来源于stack exchange,提问作者imfbnt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 13:10:23