렌더링 무작정 객체화 하기

ounols·2022년 12월 6일
0

CSEngine 프로젝트

목록 보기
17/17
최종적으로 렌더링을 객체화 한 뒤 구현한 반투명 오브젝트들

오랜만에 게임 엔진 관련한 내용을 올려봅니다.
이번엔 지금까지 무작정 절차적으로 쌓아왔던 렌더링 로직을 크게 바꾸는 시간을 가졌습니다.

어떤 고민을 하면서 어떻게 렌더링을 객체화 했는지 적어보도록 하겠습니다!

1. 갈 길 잃은 깊이버퍼와 디퍼드 렌더링

△ 모든 렌더링을 죄다 때려박은 RenderMgr 클래스△ 갈 곳 잃어서 LightMgr에 있는 그림자 버퍼

렌더링에서의 지금까지의 문제는 정말 많았습니다.
정말 많지만 그 중 핵심 요소만 꺼내와보겠습니다.

먼저 첫번째, 구현에만 힘을 계속 써서 그런지 RenderMgr이라는 렌더링을 총괄하는 클래스에선 모든 렌더링을 담당하고 있었습니다...!

정말 매우 부끄러운 클래스이지만 나름 업보이니 달게 받아야 한다는 느낌으로 링크를 걸었지만...
너무 부끄럽네요 흑흑...

그리고 두번째, 깊이 버퍼와 관련된 렌더링은 엉뚱하게도 LightMgr에서 관리하고 있었습니다.
깊이 버퍼는 라이팅에 필요한 요소이기도 하지만 일반적인 렌더링에서도 자주 사용하기 때문에 LightMgr에 넣기엔 너무 생뚱 맞습니다.

사실 어디에 어떻게 넣어야 할지 잘 몰라서 그나마 자주 사용하는 LightMgr에 넣어놨는데...
엉뚱한 만큼 코드를 짜면서도 이상한 느낌을 많이 받았습니다.

2. 왜 이런 코드를 짜게 되었나?

앞서 설명드렸던 것 처럼 구현에만 힘을 써서 이지경이 난 것도 있지만

그보다 더 중요한 문제점이 있습니다.
바로 OOP의 상속성에 의존해서 렌더링 객체 리스트 형태가 하나밖에 없었기 때문입니다.

위 사진을 보시면 렌더링 할 오브젝트를 모은 리스트가 하나만 존재합니다.
해당 리스트는 레이어와 쉐이더로 2단계로 구분되어있습니다.

이는 포워드 렌더링에 적합한 형태로 볼 수 있지만
나머지 렌더링 기법은 이러한 형태로 절대 적용할 수 없었습니다.
왜냐하면 각자 다양한 형태의 로직을 구성하기 때문입니다.

그러다보니 객체화는 점점 미궁에 빠져들고 절차적으로 구현하며 하나의 클래스에 다 때려박게 됩니다...

3. 각 렌더링을 그룹화 해보자!

그러다 생각하게 된 방식이 바로
'각 렌더링을 그룹화 시켜서 좀 더 상위 개념을 하나 만들자!'
입니다.

저는 여기서 OOP의 상속성은 인터페이스로만 적용된 렌더 그룹이라는 개념을 생성하게 됩니다.
렌더 그룹이라는 개념은 SRenderGroup이란 베이스 클래스가 존재하는데
이 클래스는 거의 인터페이스 또는 유틸의 작업만 진행합니다.

이는 아래의 코드에서 확인하실 수 있습니다.

    class SRenderGroup {
    public:
        SRenderGroup(const RenderMgr& renderMgr) : m_renderMgr(&renderMgr) {}
        virtual ~SRenderGroup() = default;

        virtual void RegisterObject(SIRender* object) = 0;

        virtual void RemoveObjects(SIRender* object) = 0;

        virtual void RenderAll(const CameraBase& camera) const = 0;

        virtual void Exterminate() = 0;

    protected:
        /**
         * 렌더링 직전의 소스 버퍼를 바인딩 하는 함수입니다.
         * @param buffer 바인딩 할 SFrameBuffer를 지정합니다.
         * @param handle SourceBuffer의 uniform id입니다.
         */
        static void BindSourceBuffer(const SFrameBuffer& buffer, const GLProgramHandle& handle, int layout);

    protected:
        const RenderMgr* m_renderMgr;
    };

SRenderGroup 클래스는 렌더링 그룹을 나타내는 클래스입니다.
이 클래스는 렌더링 매니저(RenderMgr)의 참조를 저장하고 있으며, 렌더링 그룹에 렌더링 오브젝트를 등록, 제거, 렌더링하는 메서드를 제공합니다.

이는 각 렌더링 그룹이 어떤 방식으로 렌더링 오브젝트를 관리할지, 렌더링을 수행할지에 대한 것을 상속받은 클래스에서 정의할 수 있도록 해줍니다.

이제 각 렌더링 로직은 저 클래스를 상속받아 자기 할꺼만 하고 RenderAll() 함수로 렌더링 로직을 공유합니다.

이렇게 새로 설계해서 만든 결과....
정말 객체화가 되었습니다!

4. 객체화 그 이후...

객체화가 모두 끝나자 다음과 같은 구현이 가능해졌습니다!

  • 각 렌더링 간 스왑이 편리해지면서 쓸데없는 프레임버퍼의 Blit 연산이 줄었습니다.
  • 각 렌더링 로직 간 포스트 프로세싱이 가능해졌습니다. (사진의 반투명 오브젝트가 예시)
  • 각 렌더링 로직의 활성화 여부 확인이 간편해서 쓸데없는 연산을 줄일 수 있습니다.
  • 더 심층적인 렌더링이 가능해졌습니다.

정말 많았던 일부 제약이 풀리면서 만족스러운 느낌을 받았습니다. 너무 좋네요....ㅎㅎㅎ

그리고 위의 사진은 하나의 패스를 가지고 반투명 오브젝트를 그리는 예제를 구현한 것입니다.
예제가 궁금하시다면 깃허브에서 확인해주세요!

5. 다음에 할 일

기존 코드 (왼쪽), ChatGPT와의 코드리뷰를 통해 코드의 리펙토링을 진행된 코드 (오른쪽)

요즘 ChatGPT를 통해 코드리뷰를 진행하며 엔진의 일부 코드들을 리펙토링할 예정입니다.
코드리뷰를 하면서 배운 점이 많았는데 배웠던 기술이나 스킬들을 정리해보려고 합니다.

ChatGPT가 생각보다 너무 똑똑해서 vulkan을 공부할 때 같이 써도 된다는 느낌을 크게 받았습니다.

앞으로의 코드리뷰도 기대가 됩니다!


긴 글 읽어주셔서 감사합니다.
지금까지의 내용을 다룬 모든 소스코드는 아래의 링크에서 확인하실 수 있습니다!

📣 프로젝트 Git 주소 : https://github.com/ounols/CSEngine

profile
(게임 엔진 프로그래머가 되고싶은) 게임 클라이언트 프로그래머

0개의 댓글