본문 바로가기

Frontend

[R3F] 3D 객체 겹쳐보이는 이슈 z-fighting 해결하기

문제

  • R3F 라이브러리를 사용하여 3D 맵을 구현하던 중 시점에 따라 3D 객체의 색상이 다르게 보이는 이슈 발견
  • 기본 색상은 회색인데 시점 변경 시 바닥면이 검정색으로 보이는 이슈가 있다.

원인 파악

1. 빛의 노출

빛의 노출 정도에 따라 3D 객체의 색상이 다르게 보이는 문제인줄 알고, 빛의 노출에 관계없이 색상을 렌더링하는 <meshBasicMaterial>을 사용했다.

아예 <ambientLight>를 제거하여 빛을 제거해보았지만 동일한 이슈가 발생했다.

결국 빛의 노출이 원인이 아니라고 생각이 들어 다른 방법으로 생각해보았다.

2. Canvas 설정값 수정

3D 객체를 띄워주는 Scene 역할을 하는 Canvas의 기본설정을 바꿔보았다.

Linear, RenderSide 등 렌더링 방식을 수정해보았지만 문제는 해결되지 않았다.

3. 바닥면과의 겹치는 부분 노출 방식

3D 맵의 시점을 요리조리 움직이다가 맵의 바닥면을 렌더링하는 <Floor /> 컴포넌트를 주석처리 해보았다.

바닥면을 제거하니 시점을 아무리 변경하여도 3D 객체들의 색상이 동일하게 유지되는 것을 확인할 수 있었다.

 

 

하지만 바닥면을 없애면 UI가 모호한 부분이 생기므로 바닥면을 그리면서 이슈를 해결하는 방법을 찾아야한다.

문제 해결

바닥면과 3D 객체 사이에 렌더링이 되면서 겹쳐지는 부분이 생기게 되는데, 이 때 화면에서 어떻게 렌더링할 지 모호하여 색상이 다르게 나타나는거라고 생각했다.

이러한 현상을 3D 그래픽에서는 "z-fighting"이라고 한다.

"z-fighting"이란, 3D 그래픽에서 2개 이상의 객체가 비슷한 z값(깊이값)을 가질 때 발생하는 현상으로, GPU가 어떤 객체를 앞에 그려야 할지 결정하지 못해 깜빡이거나 겹쳐 보이는 시각적 이슈가 발생한다.

이를 해결하기 위해서는 렌더링 순서를 조작하여 Floor를 먼저 렌더링하고 그 이후의 요소들을 렌더링하여 해결할 수 있다.

export const Floor = () => {
  const { isDarkMode } = useDarkmode();

  const { color, opacity } = useMemo(
    () => getThreeJSColor('--ui-base-stroke'),
    [isDarkMode],
  );

  return (
    <mesh
      rotation={[-Math.PI / 2, 0, 0]}
      position={[0, FLOOR.Y_POSITION, 0]}
      renderOrder={-1} // Render 순서 우선 순위로 설정
    >
      <planeGeometry args={FLOOR.SIZE} />
      <meshBasicMaterial color={color} transparent opacity={opacity} />
    </mesh>
  );
};

 

 

renderOrder는 기본값이 0이므로, <Floor /> 컴포넌트만 -1 값으로 설정하면 해당 컴포넌트만 우선적으로 렌더링하게 된다.

결과