dev
SR Team Project(모작)
Date: 2025-04-17 05:58
Update: 2025-10-29 08:40


Software Rendering Team Project

모작 대상: Project Warlock

bookmark

작업 인원: 4명

작업 기간: 2025-02-27 ~ 2025-04-17 (약 6주)

기술 스택

  1. C++
  2. DirextX9(DX9)

사용한 협업 툴

  1. Notion
  2. GitHub

외부 라이브러리

  1. FMOD Studio
  2. IMGUI
  3. ImGizmo
  4. json

맡은 역할

  1. 렌더링 + 쉐이더
  2. 이펙트, 파티클
  3. 사운드

개요

4명의 인원으로 약 한 달 동안 진행한 팀 프로젝트로 상용 엔진 없이 작업했으며, 맡은 파트는 프로젝트 전반적인 렌더링 요소들과, 게임에 사용될 이펙트와 파티클을 구현하는 역할을 맡았다. 그 외로 사운드 라이브러리를 준비하고 컴포넌트를 작성했다.

프레임워크

Entity-Component-System(ECS) 에 가까운 프레임워크를 작성하여 사용했으며 필요한 시스템들을 싱글턴 으로 선언했고, Engine DLL을 따로 작성하여 두개의 프로젝트로 관리했다. 대부분의 기초 컴포넌트와 시스템들을 엔진에서 작성후 동적 라이브러리화 해서 클라이언트에서 접근 가능하게 했으며 모든 시스템을 가지고 있는 싱글턴 인스턴스 하나만 접근 가능하게 했다. 고정 렌더링 파이프 라인과 쉐이더를 혼용해서 사용했으며, 필요한 객체에 쉐이더 컴포넌트를 추가해서 쉐이더에게 렌더링 과정을 넘겼다.

렌더

렌더 대상의 그룹을 나누어서 순서를 분리해 각기 다른 렌더 상태를 이용하여 화면을 구성한다.

대략적인 그룹과 순서는 다음과 같다.

1. Priority
2. Non_Blend
3. Blend
4. UI

1. Priority

이 프로젝트에서는 하늘과 같이 항상 위치하는 배경을 그리기 위해 사용했다.

사용한 옵션은

RenderState Z Enable = false;
RenderState Z Write = false;

Z값을 사용하지 않고, 작성하지 않아서 다른 객체들의 렌더링에 영향을 주지 않는다.

2. Non_Blend

RS_LIGHTING = false;
RS_ZEnable = true;
RS_ZWrite = true;

대부분의 게임 오브젝트를 렌더링 하는데 사용한 그룹이며 알파 블렌드를 진행하지 않는 객체들의 집합이다.

빛 연산은 전반적으로 꺼두고, 필요한 객체에서 따로 켜서 사용한다.

Z값을 쓰고 읽어 들여, 객체들이 다른 객체에 가려질 수 있게 한다.

3. Blend

	RS_LIGHTING = FALSE;
	RS_CULLMODE = D3DCULL_NONE;
	RS_ALPHATESTENABLE = TRUE;
	RS_ALPHAFUNC = D3DCMP_GREATER; // 알파 값이 기준보다 크면 픽셀 렌더링
	RS_ALPHAREF = 200; // 기준값 설정 (0~255)

이펙트와 파티클과 같이 알파 블렌드를 적용하고, 정렬하여 먼 객체부터 렌더하여

m_RenderObjects[RG_BLEND].sort([cameraTransform](CGameObject* a, CGameObject* b) -> bool {
					// 입력 유효성 검사 (선택 사항이지만 권장)
					if (!a || !b) return false; // 혹은 다른 규칙 적용 (예: nullptr을 뒤로 보내기)
					CTransform* transformA = static_cast<CTransform*>(a->Get_Component(L"Com_Transform"));
					CTransform* transformB = static_cast<CTransform*>(b->Get_Component(L"Com_Transform"));
					if (!transformA || !transformB) return false; // 위와 동일

					// 각 게임 오브젝트의 월드 위치 가져오기
					_float3 posA = transformA->Get_State(CTransform::STATE_POSITION);
					_float3 posB = transformB->Get_State(CTransform::STATE_POSITION);
					_float3 posC = cameraTransform->Get_State(CTransform::STATE_POSITION);

					// 카메라로부터 각 오브젝트까지의 거리 '제곱' 계산
					// 제곱 거리를 사용하는 이유: sqrt() 함수 호출보다 훨씬 빠르며, 크기 비교 목적으론 충분함
					// YourVectorType은 D3DXVECTOR3 또는 사용하는 벡터 라이브러리에 맞춰 변경

					_float3 CtoA = posC - posA;
					_float3 CtoB = posC - posB;

					_float distSqA = D3DXVec3LengthSq(&CtoA); // 아래 Helper 함수 참조
					_float distSqB = D3DXVec3LengthSq(&CtoB); // 아래 Helper 함수 참조

					// back-to-front 정렬: 거리 제곱이 큰 쪽(더 먼 쪽)이 먼저 오도록 함
					return distSqA > distSqB;
					// front-to-back 정렬 (가까운 순서)을 원하면: return distSqA < distSqB;
					});
			}

4. UI

직교 투영으로 뷰스페이스에서 고정된 위치에 표현될 UI들을 나누기 위한 그룹이다.

	RS_ALPHABLENDENABLE = TRUE;
	RS_SRCBLEND = D3DBLEND_SRCALPHA;
	RS_DESTBLEND = D3DBLEND_INVSRCALPHA;

쉐이더

  1. VS(Vertex Shader)
  2. PS(Pixel Shader)
  3. Technic
  4. Pass
  5. Double sided Rect
  6. Fog

이펙트와 파티클

여기에 각 이펙트와 파티클에 대한 이미지 짧은 gif

이펙트는 형태마다 각기 다른 로직을 사용하여 작성했으며 작업한 이펙트는 8개가 있다.

대부분의 이펙트는 빌보드를 적용하였다.

이펙트는 시간이 지나면 사라지게 했다.

파티클

파티클을 컴포넌트로 작성하여, 게임 오브젝트에 추가 할 수 있게 만들었다.

대부분의 파티클은 포인트 스프라이트로 그리기 때문에 함수 호출을 줄이기위해 세그먼트 방식을 채용했다.

  1. Snow
  2. Hit

BloodStain

맞은 적의 위치에서 바닥의 높이를 구해서 텍스쳐를 가진 이펙트를 생성했다. 약간의 범위를 지정해서 랜덤으로 오프셋을 지정해주고 변수로 넣은 만큼 반복해서 생성해줬다.

  1. Hit

총알이 투사체가 아니기때문에, 눈으로 볼수가 없어, 벽에 맞았을 경우 맞았다고 판단된 벽 위에 이펙트를 생성하고 애니메이션을 재생했다.

  1. Staff Bullet

스태프로 쏘는 투사체에는 잔상을 재현하기 위해 꼬리가 달려있었는데 이를 표현하기 위해서 투사체의 진행방향의 뒷쪽으로 항상 따라다니는 로컬 좌표의 점을 하나 찍어서 진행방향과 업벡터를 외적하여 양옆의 점을 구하고 이를 통하여 quad

  1. BulletShell
  2. Blood
  3. Gib
  4. Snow
  5. Projectile

사운드

FMOD Studio 사용으로 사운드 환경을 구축했고,

Sound Listner, Sound Source, Sound Manager, Sound Event 등을 사용하여 FMOD의 자료형에 대한 직접적인 접근 보다 사용자 정의된 함수를 팀원들에게 제공하여 사운드 컴포넌트를 사용하여 조작을 할 수 있게 만들었다.

더 해봤으면 좋을 점

  1. 인스턴싱(파티클 최적화)
  2. 쉐이더 후처리(블룸효과, Deferred Shading)