April 12, 2025

AntiThree(4) - Bruno Simon 20K


Bruno Simon 20K

🔥 可利用概念

clipboard.png

<Environment resolution={32}>
  <Lightformer position={[10, 10, 10]} scale={10} intensity={4} />
  <Lightformer position={[10, 0, -10]} scale={10} color="red" intensity={6} />
  <Lightformer position={[-10, -10, -10]} scale={10} intensity={4} />
</Environment>
<AccumulativeShadows
  temporal                 // 啟用時間累積陰影(每幀都稍微不一樣)
  frames={Infinity}        // 無限幀,會一直加疊(畫面越久越真實)
  alphaTest={1}            // 遮罩透明的 alpha 門檻(通常 1 表示完全不透明)
  blend={200}              // 幀之間的融合速度,越高越平滑
  limit={1500}             // 陰影累積最大幀數
  scale={25}               // 陰影接地面的範圍
  position={[0, -0.05, 0]} // 接地面的位置
>
const instances = Array.from({ length: count }, (_, i) => ({
  key: i,
  position: [rand(2) + 1, 10 + i / 2, rand(2) - 2],
  rotation: [Math.random(), Math.random(), Math.random()],
}));

<InstancedRigidBodies instances={instances} colliders="hull">
  <instancedMesh
    receiveShadow
    castShadow
    args={[undefined, undefined, count]} // new InstancedMesh(geometry, material, count)
    dispose={null}
  >
    {/* Merging the hat into one clump bc instances need a single geometry to function */}
    <Geometry useGroups>
      <Base geometry={nodes.Plane006.geometry} material={materials.Material} />
      <Addition
        geometry={nodes.Plane006_1.geometry}
        material={materials.boxCap}
      />
    </Geometry>
  </instancedMesh>
</InstancedRigidBodies>;
{
  key: string | number,     // 唯一識別用
  position?: [x, y, z],     // 初始位置
  rotation?: [x, y, z],     // 初始旋轉(單位是 radians)
  scale?: [x, y, z],        // 可選:縮放
  // velocity, angularVelocity...(進階物理屬性)
}
功能/特性<Geometry><group>
基本用途合併多個幾何體為單一幾何體組織對象的層級結構
幾何數據處理合併頂點、面和索引緩衝區不合併幾何數據,僅組織引用
渲染方式作為單一對象渲染每個子元素單獨渲染
Draw Calls減少 (單一調用)較多 (每個子元素一次)
實例化支持✅ 可直接實例化❌ 整組無法直接實例化
內存使用較高 (存儲合併數據)較低 (僅存儲引用)
靈活性子物體位置固定於合併時子物體可動態調整位置
材質應用可對不同部分應用不同材質每個子對象可有獨立材質
適用場景需要實例化的靜態對象需要動態調整的組合對象
性能優化適合大量重複對象適合需要單獨控制的對象
編輯難度合併後難以編輯單個部分可輕鬆編輯各個子部分
import { MathUtils } from "three";
import { Canvas } from "@react-three/fiber";
import {
  useGLTF,
  AccumulativeShadows,
  RandomizedLight,
  OrbitControls,
  Environment,
  Lightformer,
} from "@react-three/drei";
import {
  EffectComposer,
  DepthOfField,
  N8AO,
  ToneMapping,
} from "@react-three/postprocessing";
import { Geometry, Base, Addition, Brush } from "@react-three/csg";
import {
  Physics,
  RigidBody,
  CuboidCollider,
  InstancedRigidBodies,
} from "@react-three/rapier";

export const App = () => (
  <Canvas
    flat
    shadows
    gl={{ antialias: false }}
    camera={{ position: [-30, 35, -15], near: 30, far: 55, fov: 12 }}
  >
    {/* Lighting, environment and colors */}
    <color attach="background" args={["#f0f0f0"]} />
    <ambientLight intensity={0.5} />
    <directionalLight
      position={[-10, 10, 5]}
      shadow-mapSize={[256, 256]}
      shadow-bias={-0.0001} // 解決陰影失真問題
      castShadow
    >
      <orthographicCamera attach="shadow-camera" args={[-10, 10, -10, 10]} />
    </directionalLight>
    <Environment resolution={32}>
      <Lightformer position={[10, 10, 10]} scale={10} intensity={4} />
      <Lightformer
        position={[10, 0, -10]}
        scale={10}
        color="red"
        intensity={6}
      />
      <Lightformer position={[-10, -10, -10]} scale={10} intensity={4} />
    </Environment>
    {/* Moon physics */}
    <Physics gravity={[0, -4, 0]}>
      <Scene position={[1, 0, -1.5]} />
      <Hats />
      // 物理平面
      <RigidBody position={[0, -1, 0]} type="fixed" colliders="false">
        <CuboidCollider restitution={0.1} args={[1000, 1, 1000]} />
      </RigidBody>
    </Physics>
    {/* Soft shadows, they stop rendering after 1500 frames */}
    <AccumulativeShadows
      temporal
      frames={Infinity}
      alphaTest={1}
      blend={200}
      limit={1500}
      scale={25}
      position={[0, -0.05, 0]}
    >
      <RandomizedLight
        amount={1}
        mapSize={512}
        radius={5}
        ambient={0.5}
        position={[-10, 10, 5]}
        size={10}
        bias={0.001}
      />
    </AccumulativeShadows>
    {/* Effects */}
    <EffectComposer>
      <N8AO aoRadius={0.5} intensity={1} />
      <DepthOfField target={[0, 0, -2.5]} focusRange={0.1} bokehScale={10} />
      <ToneMapping />
    </EffectComposer>
    {/* Controls */}
    <OrbitControls
      autoRotate
      autoRotateSpeed={0.1}
      enablePan={false}
      enableZoom={false}
      minPolarAngle={Math.PI / 4}
      maxPolarAngle={Math.PI / 4}
    />
  </Canvas>
);

function Scene(props) {
  const { nodes, materials } = useGLTF(
    "/blender-threejs-journey-20k-transformed.glb"
  );
  return (
    <group {...props} dispose={null}>
      <RigidBody type="fixed" colliders="trimesh">
        <mesh
          castShadow
          receiveShadow
          geometry={nodes.boxBase.geometry}
          material={materials.boxBase}
        />
        <mesh
          receiveShadow
          geometry={nodes.boxBack.geometry}
          material={materials.inside}
        />
        <mesh
          castShadow
          receiveShadow
          geometry={nodes.Text.geometry}
          material={materials.boxBase}
        />
      </RigidBody>
    </group>
  );
}

function Hats({ count = 80, rand = MathUtils.randFloatSpread }) {
  const { nodes, materials } = useGLTF(
    "/blender-threejs-journey-20k-hat-transformed.glb"
  );
  const instances = Array.from({ length: count }, (_, i) => ({
    key: i,
    position: [rand(2) + 1, 10 + i / 2, rand(2) - 2],
    rotation: [Math.random(), Math.random(), Math.random()],
  }));
  return (
    <InstancedRigidBodies instances={instances} colliders="hull">
      <instancedMesh
        receiveShadow
        castShadow
        args={[undefined, undefined, count]}
        dispose={null}
      >
        {/* Merging the hat into one clump bc instances need a single geometry to function */}
        <Geometry useGroups>
          <Base
            geometry={nodes.Plane006.geometry}
            material={materials.Material}
          />
          <Addition
            geometry={nodes.Plane006_1.geometry}
            material={materials.boxCap}
          />
        </Geometry>
      </instancedMesh>
    </InstancedRigidBodies>
  );
}