Trails

Starter pack

트레일의 세계로 들어가 봅시다! 트레일은 장면에 움직임의 감각을 더하기에 훌륭한 방법입니다. 빛의 흔적, 연기의 흔적 또는 움직이는 물체의 흔적과 같은 다양한 효과를 생성하는 데 사용할 수 있습니다.

다 함께 만들 최종 프로젝트는 다음과 같습니다:

먼저 커스텀 트레일 커서를 사용하여 간단한 트레일 효과를 만드는 것부터 시작하겠습니다. 그런 다음 프리뷰에서 본 혜성을 만들기 위해 dreiTrail component를 탐색하겠습니다.

스타터 프로젝트

스타터 프로젝트에는 이전 강의에서 이미 다룬 많은 내용이 포함되어 있습니다:

또한 Tailwind CSS를 사용하여 UI를 빠르게 디자인했습니다. Tailwind CSS에 익숙하지 않다면 UI 부분을 건너뛰고 Three.js 부분에 집중할 수 있습니다.

WawaCoinWawaCard 모델은 자체 제작되었으며 스타터 프로젝트에서 사용할 수 있습니다. dreiMeshTransmissionMaterial을 사용하여 이러한 미래지향적인 모습을 만들었습니다.

장면을 원하는 대로 변형해보세요. 프로젝트의 어떠한 부분도 자유롭게 재사용하여 자신의 프로젝트에 사용할 수 있습니다.

말하는 것을 깜빡했지만, 웹사이트의 내용은 순전히 허구입니다. 새로운 암호화폐를 출시하려는 것은 아닙니다. (아직은? 👀)

사용자 지정 트레일 커서

커서를 따라가는 간단한 트레일 효과를 만들어 보겠습니다.

새로운 components/Cursor.jsx 파일을 생성하고 다음 코드를 추가하세요:

import { useFrame } from "@react-three/fiber";
import { useControls } from "leva";
import { useRef } from "react";
export const Cursor = () => {
  const { color, intensity, opacity, size } = useControls("Cursor", {
    size: { value: 0.2, min: 0.1, max: 3, step: 0.01 },
    color: "#dfbcff",
    intensity: { value: 4.6, min: 1, max: 10, step: 0.1 },
    opacity: { value: 0.5, min: 0, max: 1, step: 0.01 },
  });
  const target = useRef();
  useFrame(({ clock }) => {
    if (target.current) {
      const elapsed = clock.getElapsedTime();
      target.current.position.x = Math.sin(elapsed) * 5;
      target.current.position.y = Math.cos(elapsed * 2) * 4;
      target.current.position.z = Math.sin(elapsed * 4) * 10;
    }
  });
  return (
    <>
      <group ref={target}>
        <mesh>
          <sphereGeometry args={[size / 2, 32, 32]} />
          <meshStandardMaterial
            color={color}
            transparent
            opacity={opacity}
            emissive={color}
            emissiveIntensity={intensity}
          />
        </mesh>
      </group>
    </>
  );
};

이 코드는 사인파를 따르는 간단한 구체를 만들어줍니다. Leva controls를 사용하여 커서의 크기, 색상, 밝기, 불투명도를 조절할 수 있습니다.

현재는 고정된 움직임을 사용하고 있습니다. 이는 트레일 시각화를 단순화합니다. 나중에 마우스 위치로 교체할 것입니다.

Experience 컴포넌트에 Cursor 컴포넌트를 추가하세요:

// ...
import { Cursor } from "./Cursor";

export const Experience = () => {
  // ...
  return (
    <>
      <Cursor />
      {/* ... */}
    </>
  );
};

// ...

움직이는 구체를 볼 수 있으며, 이는 트레일의 대상이 될 것입니다.

SimpleTrail 컴포넌트

group는 우리의 trail이 따라갈 타겟입니다. 우리는 trail 효과를 생성하기 위해 새로운 컴포넌트 components/SimpleTrail.jsx를 만들 것입니다:

import { useRef } from "react";
import * as THREE from "three";

export function SimpleTrail({
  target = null,
  color = "#ffffff",
  intensity = 6,
  numPoints = 20,
  height = 0.42,
  minDistance = 0.1,
  opacity = 0.5,
  duration = 20,
}) {
  const mesh = useRef();

  return (
    <>
      <mesh ref={mesh}>
        <planeGeometry args={[1, 1, 1, numPoints - 1]} />
        <meshBasicMaterial
          color={color}
          side={THREE.DoubleSide}
          transparent={true}
          opacity={opacity}
          depthWrite={false}
        />
      </mesh>
    </>
  );
}

매개변수는 다음과 같습니다:

  • target: 따라가기 위한 타겟의 ref.
  • color: trail의 색상.
  • intensity: trail의 발광 강도.
  • numPoints: trail에 저장할 위치의 수. (숫자가 클수록 trail이 길어집니다).
  • height: trail의 높이.
  • minDistance: 두 점 사이의 최소 거리.
  • opacity: trail의 불투명도.
  • duration: trail이 끝에서부터 사라지기 시작하는 시간.

모든 매개변수를 아직 이해하지 못해도 걱정하지 마세요. 우리는 trail을 구현하면서 설명하도록 하겠습니다.

Cursor 컴포넌트에 SimpleTrail 컴포넌트를 임포트하세요:

// ...
import { SimpleTrail } from "./SimpleTrail";

export const Cursor = () => {
  // ...

  return (
    <>
      <group ref={target}>{/* ... */}</group>
      <SimpleTrail
        target={target}
        color={color}
        intensity={intensity}
        opacity={opacity}
        height={size}
      />
    </>
  );
};

meshnumPoints와 동일한 세그먼트 수를 가진 <planeGeometry />로 구성되어 있습니다. 우리는 타겟을 따라가기 위해 각 세그먼트의 위치를 업데이트할 것입니다.

SimpleTrail

비주얼적으로 우리의 plane 크기는 1x1이기 때문에, 우리는 사각형을 볼 수 있지만, 세그먼트의 수 덕분에 trail 효과를 만들기 위해 vertices를 조작할 수 있습니다.

다음은 한 세그먼트를 가진 plane과 20 세그먼트를 가진 plane을 나란히 비교한 것입니다:

<group position-x={5}>
  <mesh position-x={4} scale-y={5}>
    <planeGeometry args={[1, 1, 1, numPoints - 1]} />
    <meshBasicMaterial color={"red"} wireframe />
  </mesh>
  <mesh position-x={2} scale-y={5}>
    <planeGeometry args={[1, 1, 1, 1]} />
    <meshBasicMaterial color={"red"} wireframe />
  </mesh>
</group>

이 코드는 개념 이해용으로만 사용됩니다. 개념을 이해한 후에는 제거할 수 있습니다.

우리는 y축으로 그들을 스케일링하여 세그먼트 수의 차이를 볼 수 있습니다.

Representation of the segments

왼쪽 plane은 4개의 vertices만 가지고 있는 반면, 오른쪽 plane은 더 많은 수의 vertices를 가지고 있습니다. 우리는 이 vertices를 조작하여 trail 효과를 만들 것입니다.

우리는 trail을 만들기 위해 line을 사용할 수도 있지만, plane을 사용하면 보다 흥미로운 효과를 만들 수 있습니다 (예를 들어 바람에 더 잘 작동합니다).

dreiTrail 컴포넌트는 line을 사용합니다. 우리는 같은 것을 재구현하고 싶지 않습니다.

버텍스 조작하기

우리는 시간이 지남에 따라 plane의 버텍스 위치를 target을 따라가도록 업데이트할 것입니다.

먼저, 우리는 target의 모든 위치를 배열에 저장해야 할 것입니다. 위치를 저장하기 위해 ref를 사용할 것입니다.

// ...
import * as THREE from "three";

export function SimpleTrail(
  {
    // ...
  }
) {
  const mesh = useRef();
  const positions = useRef(
    new Array(numPoints).fill(new THREE.Vector3(0, 0, 0))
  );
  // ...
}

이 배열은 항상 numPoints 길이를 가지며 target의 위치를 저장할 것입니다.

target이 움직일 때, 우리는 배열의 앞에 새로운 위치를 추가하고 다른 위치들을 뒤로 밀어냅니다.

Graph explaining the position array

이것을 구현하기 위해, 우리는 버텍스의 위치를 업데이트하기 위해 useFrame 훅을 사용할 것입니다.

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

export function SimpleTrail(
  {
    // ...
  }
) {
  // ...

  useFrame(() => {
    if (!mesh.current || !target?.current) {
      return;
    }

    const curPoint = target.current.position;
    const lastPoint = positions.current[0];

    const distanceToLastPoint = lastPoint.distanceTo(target.current.position);

    if (distanceToLastPoint > minDistance) {
      positions.current.unshift(curPoint.clone());
      positions.current.pop();
    }
  });

  // ...
}

먼저, 우리는 마지막 포인트와 현재 포인트 사이의 거리를 계산합니다. 거리가 minDistance보다 크면, 우리는 현재 포인트를 unshift로 배열의 앞에 추가하고 pop으로 마지막 포인트를 제거합니다.

이제 우리는 target의 위치를 따라 plane의 버텍스 위치를 업데이트해야 합니다.

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.