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

如何在Three.js中实现父物体内子物体的溢出裁剪?

问题:Three.js实现父物体外部子物体隐藏(类似CSS overflow:hidden)

我是Three.js新手,想请教是否可以裁剪或隐藏父物体外部的子物体?例如我有一个盒子,内部有一个球体(如图:球体在盒子内部),希望隐藏球体超出盒子的部分,实现类似CSS中overflow:hidden的效果。

以下是我的示例代码:

// Simple example
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls";

let renderer, scene, camera, controls;

init();
animate();

function init() {
    // renderer
  renderer = new THREE.WebGLRenderer({alpha:true, antialias:true});
  renderer.autoClear = false;
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );
  console.log(renderer)
    // scene
  scene = new THREE.Scene();

    // camera
  camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
  camera.position.set(0, 0, 40);

    // controls
  controls = new OrbitControls( camera, renderer.domElement );
    const materialA =  new THREE.MeshStandardMaterial({
        roughness: 0,
        metalness: 0,
        color: 0xffffff,
    })
  const materialB = new THREE.MeshPhysicalMaterial({
        roughness: 0,
        transmission: 1,
        thickness: 0.5
    });
  const sphere = new THREE.Mesh(new THREE.SphereGeometry(-5,30,30),materialA);
  sphere.position.set(5, 0, 0)
  
  const box = new THREE.Mesh(new THREE.BoxGeometry(12,12,12),materialB);
   scene.add(box);
  
  const light = new THREE.DirectionalLight('white', 1);
  light.position.set(10, 10, 0);
  const light2 = new THREE.DirectionalLight('white', 1);
  light2.position.set(-10, -10, -10);
  scene.add(sphere, box,light,light2);
}

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

解决方案:使用剪裁平面(Clipping Planes)

Three.js可以通过**剪裁平面(THREE.Plane)**实现这种局部裁剪效果,原理是创建六个平面模拟盒子的六个面,让渲染器只渲染盒子内部的部分。

修改后的代码示例

// Simple example
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls";

let renderer, scene, camera, controls;

init();
animate();

function init() {
    // renderer
  renderer = new THREE.WebGLRenderer({alpha:true, antialias:true});
  renderer.autoClear = false;
  renderer.setSize( window.innerWidth, window.innerHeight );
  // 开启局部剪裁支持
  renderer.localClippingEnabled = true;
  document.body.appendChild( renderer.domElement );

    // scene
  scene = new THREE.Scene();

    // camera
  camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
  camera.position.set(0, 0, 40);

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

  // 创建盒子对应的六个剪裁平面
  const boxHalfSize = new THREE.Vector3(6,6,6); // 对应BoxGeometry(12,12,12)的半尺寸
  const clipPlanes = [
    new THREE.Plane(new THREE.Vector3(1,0,0), boxHalfSize.x),   // 右平面
    new THREE.Plane(new THREE.Vector3(-1,0,0), boxHalfSize.x),  // 左平面
    new THREE.Plane(new THREE.Vector3(0,1,0), boxHalfSize.y),   // 上平面
    new THREE.Plane(new THREE.Vector3(0,-1,0), boxHalfSize.y),  // 下平面
    new THREE.Plane(new THREE.Vector3(0,0,1), boxHalfSize.z),   // 前平面
    new THREE.Plane(new THREE.Vector3(0,0,-1), boxHalfSize.z),  // 后平面
  ];

    const materialA =  new THREE.MeshStandardMaterial({
        roughness: 0,
        metalness: 0,
        color: 0xffffff,
        // 给球体材质绑定剪裁平面
        clippingPlanes: clipPlanes,
        clipIntersection: true // 仅渲染所有平面交集内的部分(盒子内部)
    })
  const materialB = new THREE.MeshPhysicalMaterial({
        roughness: 0,
        transmission: 1,
        thickness: 0.5
    });
  const sphere = new THREE.Mesh(new THREE.SphereGeometry(5,30,30),materialA); // 修正负半径导致的面反转问题
  sphere.position.set(5, 0, 0)
  
  const box = new THREE.Mesh(new THREE.BoxGeometry(12,12,12),materialB);
  scene.add(box);
  
  const light = new THREE.DirectionalLight('white', 1);
  light.position.set(10, 10, 0);
  const light2 = new THREE.DirectionalLight('white', 1);
  light2.position.set(-10, -10, -10);
  scene.add(sphere, box,light,light2);
}

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

关键说明

  • 开启渲染器的localClippingEnabled属性,启用局部剪裁功能
  • 六个剪裁平面分别对应盒子的六个面,法线方向指向盒子外部,第二个参数为平面到原点的距离(即盒子的半尺寸)
  • 给需要裁剪的球体材质设置clippingPlanes为这六个平面,并开启clipIntersection: true,表示只渲染所有平面共同交集内的区域
  • 修正原代码中SphereGeometry(-5,30,30)的负半径问题,负半径会导致球体表面反转,改为正半径5

其他可选方案:CSG布尔运算

如果需要对几何体进行实体级剪裁(比如生成新的裁剪后几何体),可以使用Three.js的CSG库(如three-csg-ts),通过布尔运算直接计算出球体在盒子内部的部分。这种方式适合静态场景,动态修改会存在一定性能开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 22:02:09