Rendermanager 구조 정리

Jaemyeong Lee·2025년 4월 2일

게임 서버1

목록 보기
210/220

🧨 기존 방식의 문제점

❌ 쉐이더 변경 시 상태 초기화

RenderManager는 Init 시점에 특정 쉐이더와 관련된 ConstantBuffer를 초기화해. 그런데 쉐이더가 바뀌면 이전 상태가 날아가버려. 즉, 여러 개의 쉐이더를 동시에 쓰는 상황에 적합하지 않았어.

❌ 버퍼를 모든 렌더링 대상에게 공유

LightDesc, MaterialDesc 같은 정보는 오브젝트마다 다를 수 있어. 그런데 RenderManager는 이걸 전역으로 들고 있다 보니, 각기 다른 데이터를 사용하는 오브젝트에 대해 정확한 처리가 어려워졌지.


✅ 개선 전략: Shader 클래스에 각자 관리하도록 위임

핵심 아이디어

모든 ConstantBuffer들을 Shader 내부로 옮긴다. 즉, 각 Shader가 자신이 필요로 하는 버퍼만 관리하고, 필요할 때만 생성해서 사용하는 방식으로 변경하는 거야.


1️⃣ 구조 정리: Shader 클래스에 Push 함수와 Buffer 이전

예시: PushGlobalData()

void Shader::PushGlobalData(const Matrix& view, const Matrix& projection)
{
	if (_globalEffectBuffer == nullptr)
	{
		_globalBuffer = make_shared<ConstantBuffer<GlobalDesc>>();
		_globalBuffer->Create();
		_globalEffectBuffer = GetConstantBuffer("GlobalBuffer");
	}

	_globalDesc.V = view;
	_globalDesc.P = projection;
	_globalDesc.VP = view * projection;
	_globalDesc.VInv = view.Invert();
	_globalBuffer->CopyData(_globalDesc);
	_globalEffectBuffer->SetConstantBuffer(_globalBuffer->GetComPtr().Get());
}

핵심 포인트

  • 필요할 때만 생성한다 (lazy init).
  • 생성된 버퍼에 값을 복사하고 셰이더에 전달한다.
  • 이는 쉐이더마다 고유하게 관리되므로, 다중 쉐이더 환경에서 충돌이 없다.

다른 Push 함수들도 동일 원리 적용

  • PushTransformData
  • PushMaterialData
  • PushLightData
  • PushBoneData
  • PushKeyframeData
  • PushTweenData

2️⃣ RenderManager 제거 및 코드 클린업

🧹 RenderManager 제거 절차

  • 모든 Shader 관련 변수, 함수 → Shader로 이동
  • RenderManager에서 관련 코드 제거
  • 이름을 BindShaderDesc.h로 변경하고, 구조체 정의만 남긴다.

왜?

RenderManager가 전역 상태를 관리하는 유틸처럼 되어 있었기 때문에, 각 객체나 쉐이더가 개별적으로 관리하도록 책임 분리를 해준 거야.


3️⃣ Scene에서 Camera, Light 접근하기

왜 필요했나?

Shader에 데이터를 밀어넣는 주체가 분산되다 보니, 카메라나 라이트처럼 전역에서 사용되는 요소를 어디서든 접근 가능하게 만들 필요가 있었지.

해결 방법

Scene 클래스에 다음 함수 추가:

unordered_set<shared_ptr<GameObject>> GetObjects();
shared_ptr<GameObject> GetCamera();
shared_ptr<GameObject> GetLight();
  • 현재는 “카메라, 라이트는 하나만 있다”는 가정하에 가장 앞에 있는 걸 반환.

4️⃣ 각 렌더러에서 Shader에 직접 데이터 전달

MeshRenderer / ModelRenderer / ModelAnimator 수정

// GlobalData
_shader->PushGlobalData(Camera::S_MatView, Camera::S_MatProjection);

// LightData
auto lightObj = SCENE->GetCurrentScene()->GetLight();
if (lightObj)
    _shader->PushLightData(lightObj->GetLight()->GetLightDesc());

이제 Shader에 직접 데이터를 전달하고, 필요한 버퍼가 없다면 Shader가 알아서 만든다.


5️⃣ 테스트 및 결과

✅ Build 성공

  • 엔진 전체 빌드 성공
  • Client 테스트도 정상 동작
  • RenderManager 완전히 제거 성공

💡 변화의 핵심

이전 구조개선 구조
RenderManager가 모든 Shader 상태 관리각 Shader가 자신 상태 독립적으로 관리
전역 공유 → 충돌 가능성Shader 별 개별 관리 → 충돌 없음
항상 모든 버퍼 생성필요한 시점에만 생성 (캐시 방식)
구조체, 로직이 혼재Shader, Desc 명확하게 분리됨
확장성 부족다양한 Shader 자유롭게 사용 가능

profile
李家네_공부방

0개의 댓글