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

如何在react-three-fiber中实现类2D overflow-hidden的3D效果?

问题描述

我参考kprverse网站,尝试在3D模型中实现类2D的overflow-hidden效果,但使用裁剪平面(clipping plane)未能达成预期。

  • 原始3D模型:original model
  • 当前裁剪效果:clipped model
  • 理想效果:仅显示红线间的模型并隐藏溢出部分,如图:ideally clipped model

请问有更好的方法实现该效果吗?

代码示例

import * as THREE from "three";
import { useGLTF, OrbitControls, ScrollControls } from "@react-three/drei";

import React, { useRef } from "react";
import { Canvas } from "@react-three/fiber";

export const FLOOR_HEIGHT = 2.3;
export const NB_FLOORS = 3;

export default function TestingClipping(props) {
  const { nodes, materials } = useGLTF("./models/WawaOffice.glb");
  const planes = useRef([
    new THREE.Plane(new THREE.Vector3(-0.5, 0, 0), 1),
    new THREE.Plane(new THREE.Vector3(0.5, 0, 0), 1)
  ]);
  return (
    <Canvas
      style={{ height: "100vh", width: "100vw" }}
      gl={{ localClippingEnabled: true }}
    >
      <ambientLight intensity={1} />
      <OrbitControls enableZoom={false} enableDamping={false} />
      <ScrollControls pages={3} damping={0.25}>
        <group scale={[1, 1, 1]}>
          <group
            {...props}
            dispose={null}
            position={[0.5, -1, -1]}
            rotation={[0, -Math.PI / 3, 0]}
            style={{ transform: "scale(0.1)" }}
          >
            <mesh geometry={nodes["01_office"].geometry}>
              <meshStandardMaterial
                {...materials["01"]}
                clippingPlanes={planes.current}
              />
            </mesh>
            <group position={[0, 2.11, -2.23]}>
              <group>
                <mesh geometry={nodes["02_library"].geometry}>
                  <meshStandardMaterial
                    {...materials["02"]}
                    clippingPlanes={planes.current}
                  />
                </mesh>
              </group>
            </group>
            <group position={[-1.97, 4.23, -2.2]}>
              <group>
                <mesh geometry={nodes["03_attic"].geometry}>
                  <meshStandardMaterial
                    {...materials["03"]}
                    clippingPlanes={planes.current}
                  />
                </mesh>
              </group>
            </group>
          </group>
        </group>
      </ScrollControls>
    </Canvas>
  );
}

// useGLTF.preload("./models/WawaOffice.glb");
解决方案

1. 修正裁剪平面参数

当前裁剪平面的方向和距离设置与模型空间位置不匹配,导致裁剪范围偏差,可按以下方式调整:

  • 先获取模型在世界空间中的X轴范围,将平面的constant参数(THREE.Plane的第二个参数)设为对应红线的位置值。
  • 确保平面法向量方向正确:法向量指向的一侧会被裁剪,要保留中间区域,两个平面的法向量需分别指向左右外侧,示例:
    const planes = useRef([
      new THREE.Plane(new THREE.Vector3(1, 0, 0), 模型左侧边界X值), // 裁剪左侧外区域
      new THREE.Plane(new THREE.Vector3(-1, 0, 0), -模型右侧边界X值) // 裁剪右侧外区域
    ]);
    

2. 使用剪刀测试(ScissorTest)模拟2D裁剪

如果要完全复刻2D的overflow-hidden效果,推荐使用Three.js的剪刀测试,直接限制渲染区域在指定屏幕矩形内:
在react-three-fiber中,可通过useFrame钩子实现:

import { useFrame, useThree } from "@react-three/fiber";

export default function TestingClipping(props) {
  // ... 原有代码
  const { gl } = useThree();
  const canvas = gl.domElement;
  
  useFrame(() => {
    // 自定义屏幕裁剪区域,对应红线框的位置和尺寸
    const clipRect = { x: 200, y: 100, width: 800, height: 600 };
    // 开启剪刀测试
    gl.enable(gl.SCISSOR_TEST);
    // 注意Three.js的坐标原点在左下角,需转换Canvas的y轴坐标
    gl.scissor(clipRect.x, canvas.height - clipRect.y - clipRect.height, clipRect.width, clipRect.height);
    gl.viewport(clipRect.x, canvas.height - clipRect.y - clipRect.height, clipRect.width, clipRect.height);
  }, 1); // 设置优先级,确保在渲染前执行

  // ... 模型渲染代码
}

3. 组合两种方法优化效果

如果需要同时限制3D空间范围和屏幕渲染区域,可以将裁剪平面与剪刀测试结合:先用裁剪平面过滤模型的3D溢出部分,再用剪刀测试锁定屏幕显示区域,确保最终效果完全匹配需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 16:42:49