[실전-마당을 나온 암탉(2)]

JAMEe_·2024년 7월 18일

R3F

목록 보기
24/24

땅에 풀과 꽃 심기

100x100 의 땅에 랜덤한 위치에 풀과 꽃을 심을 예정

마당에 있는 풀들과 꽃을 여러개 생성해도
한번의 DrawCall 로 그려지도록 최적화 작업도 같이 진행

여기서 사용한 최적화 방법으로는 instancedMeshMerged 컴포넌트를 사용하였다

풀들은 materials 가 동일해서 instancedMesh 로 처리하였고
꽃봉오리와 갈대가 함께 있는 모델은 materials 가 동일하지 않아 Merged 로 처리하였다

※ 정보
모델안에 서로 다른 materials 를 지니고 있으면 모델을 한번의 Drawcall 로 나타내기에 한계가 있음
materials 가 동일해야 여러 geometry 로 구성된 모델을 한번의 Drawcall 로 나타낼 수 있음
최적화 측면에서는 instancedMesh 가 수천개 그렸을때 뛰어남

instancedMesh

먼저 instancedMesh 에 들어갈 요소들을 살펴보자

<instancedMesh
   ref={meshRef}
   args={[mergedGeometry.current, materials._4, count]}
   castShadow
/>

meshRef : 각 풀들에 대한 position, scale 등 Matrix 에서 설정할 수 있는 속성들을 설정해준다
args : 풀에 대한 모델 정보와 총 그릴 갯수를 적어준다

1. 여러개의 Geometry 로 구성된 모델을 한번의 DrawCall 로 그려주기

useEffect(() => {
    const geometries = [
      nodes.Mesh1_ElwFor_Grass01_C01_1_Model.geometry,
      nodes.Mesh2_ElwFor_Grass01_C01_2_Model.geometry,
      ...
    ];

    mergedGeometry.current = mergeBufferGeometries(geometries);
    setIsGeometryReady(true);
}, [nodes]);

다음과 같이 모델을 이루고 있는 geometry 들을 mergeBufferGeometries 을 통해 하나의 geometry 로 병합하고 정보를 mergedGeometry 에 넣어준다
이것으로 여러 geometry 로 구성된 하나의 모델을 한번의 DrawCall 로 그리는 것은 완성되었다

2. 모델들을 임의의 위치에 배치하기

useEffect(() => {
    if (isGeometryReady && meshRef.current) {
      for (let i = 0; i < count; i++) {
        dummy.scale.set(0.02, 0.02, 0.02);
        const x = (Math.random() - 0.5) * 1e2;
        const z = (Math.random() - 0.5) * 1e2;
        dummy.position.set(x, 0, z); 
        dummy.updateMatrix();
        meshRef.current.setMatrixAt(i, dummy.matrix);
      }
      meshRef.current.instanceMatrix.needsUpdate = true;
    }
}, [dummy, isGeometryReady]);

100x100 의 땅이므로 -50,50 의 범위로 설정된 랜덤 좌표를 생성하고, scale 값을 설정한 후 Matrix 를 업데이트 해주었다.

이제 1, 2번 과정을 통해 얻은 정보를 instancedMesh 에 넣어주면 모델을 여러개 배치해도 한번의 DrawCall 로 해결

Merged

먼저 Merged 에 들어갈 요소들을 살펴보자

<Merged castShadow meshes={meshes}>
  ...
</Merged>

meshes : 모델을 이루고 있는 각 geometry 의 정보

const meshes = useMemo(
    () => ({
      Dandelions1: nodes["Node-Mesh"],
      Dandelions2: nodes["Node-Mesh_1"],
    }),
    [nodes]
);

비교적 간단하므로 전체코드로 살펴보기

<Merged castShadow meshes={meshes}>
        {(mesh) => {
          return (
            <>
              {Array.from({ length: count }).map((_, idx) => (
                <Fragment key={idx}>
                  <group position={position[idx]} scale={5}>
                    <mesh.Dandelions1 material={materials.mat21} />
                    <mesh.Dandelions2 material={materials.mat9} />
                  </group>
                </Fragment>
              ))}
            </>
          );
        }}
</Merged>

앞서 설명했듯이 서로 다른 materials 로 구성된 모델은 한번의 DrawCall 로 표현하기 어려움
그래서 모델의 geometry 에 대한 DrawCall 은 여러번 진행하되,
모델을 여러개 그릴때의 DrawCall 은 한번만 실행되도록

profile
안녕하세요

0개의 댓글