전체 구조 흐름 요약
1. SceneWindow 클래스 구조 정리
SceneWindow는 에디터 내에서 씬(Scene)을 시각적으로 편집할 수 있는 뷰포트입니다.
EditorCamera를 이용해 별도의 렌더 타겟에 씬을 렌더링하고,
해당 이미지를 ImGui 위에 출력합니다.
멤버 변수 정리
멤버 | 설명 |
mEditorCameraObject | 씬뷰 전용 카메라가 부착된 GameObject |
mEditorCamera | 실제 렌더링을 담당하는 EditorCamera 컴포넌트 |
GuizmoType | 현재 활성화된 변환 Gizmo (이동, 회전, 스케일) |
ViewportBounds[2] | 뷰포트의 좌상단, 우하단 ImGui 위치 |
ViewportSize | ImGui 내 SceneView의 크기 |
ViewportFocused / Hovered | 현재 마우스 포커스 여부 (입력 판단용) |
주요 함수 역할
함수 | 설명 |
Initialize() | 에디터 카메라 객체 생성, 렌더타겟 초기화 |
Run() | 카메라 업데이트, 씬 렌더링, Gizmo 출력, ImGui 이미지 출력 |
OnGUI() | SceneView 내 추가적인 UI 편집 요소 (없으면 생략 가능) |
SetGuizmoType() | Gizmo 조작 모드 설정 |
2. GameView ImGui 렌더링 코드 설명
코드 요약:
cpp
복사편집
ImGui::Begin("Game");
ImVec2 viewportPanelSize = ImGui::GetContentRegionAvail();
ViewportSize = Vector2{ viewportPanelSize.x, viewportPanelSize.y };
ya::graphics::Texture* texture = FrameBuffer->GetAttachmentTexture(0);
ImGui::Image((ImTextureID)texture->GetSRV().Get(), ImVec2{ ViewportSize.x, ViewportSize.y });
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("PROJECT_ITEM")) {
const auto path = static_cast<const wchar_t*>(payload->Data);
OpenScene(path);
}
ImGui::EndDragDropTarget();
}
for (auto& iter : EditorWindows)
iter.second->Run();
ImGui::End();
C++
복사
핵심 동작 순서 설명
단계 | 설명 |
ImGui::Begin("Game") | GameView 창 시작 |
GetContentRegionAvail() | ImGui 내부에서 사용 가능한 영역 크기 (픽셀) 계산 |
FrameBuffer->GetAttachmentTexture(0) | 게임 카메라가 렌더링한 이미지(Texture) 가져오기 |
ImGui::Image(...) | 해당 Texture를 GameView에 출력 |
ImGui::BeginDragDropTarget() | 드래그 앤 드롭 지원 (예: 프로젝트 창에서 씬 드래그) |
EditorWindows 루프 | 에디터 내 다른 창들(Inspector, Hierarchy 등)도 실행 |
ImGui::End() | GameView 창 종료 |
씬뷰 vs 게임뷰 차이 정리
항목 | Scene View | Game View |
사용 카메라 | EditorCamera (사용자가 조작) | mainCamera (게임 로직 기준) |
렌더 타겟 | Editor 전용 RenderTarget | 공용 FrameBuffer |
목적 | 편집기에서 오브젝트 확인/이동/회전 | 최종 게임 플레이 화면 |
입력 우선순위 | Hover 여부로 차단/허용 | 별도로 관리 필요 |
Gizmo 사용 |
핵심 로직
1. 렌더 타겟(RenderTarget) 분리
목적:
•
게임 뷰와 에디터 뷰의 결과가 겹치지 않게 별도의 출력 버퍼를 사용
•
각각 다른 뷰포트 크기, 카메라 행렬을 사용하므로 완전한 분리가 필요함
구현 요약
•
EditorCamera::CreateRenderTarget(width, height)
•
SceneWindow에서는 EditorCamera의 RenderTarget 사용
•
GameView는 renderer::FrameBuffer (main camera 기준의 RT) 사용
graphics::RenderTargetSpecification spec;
spec.Attachments = { RGBA8, Depth };
mRenderTarget = new RenderTarget(spec);
C++
복사
각 뷰가 자신만의 GPU 출력 버퍼를 가지고 렌더링 → 서로 덮어쓰거나 공유하는 문제 없음
2. 카메라 행렬(View/Projection) 분리
목적:
•
SceneView는 오브젝트 조작에 최적화된 카메라 (예: 고정된 시야, 자유 회전 등)
•
GameView는 게임 안에서 실제 사용하는 시점 (캐릭터 따라다니기, 1인칭 등)
EditorCamera
// render the scene
Matrix viewMatrix = mEditorCamera->GetViewMatrix();
Matrix projectionMatrix = mEditorCamera->GetProjectionMatrix();
Vector3 cameraPos = mEditorCamera->GetOwner()->GetComponent<ya::Transform>()->GetPosition();
std::vector<ya::GameObject*> opaqueList = {};
std::vector<ya::GameObject*> cutoutList = {};
std::vector<ya::GameObject*> transparentList = {};
// collect randerables(game objects)
ya::Scene* scene = ya::SceneManager::GetActiveScene();
ya::renderer::CollectRenderables(scene, opaqueList, cutoutList, transparentList);
// soring renderables by distance (between camera and game object)
ya::renderer::SortByDistance(opaqueList, cameraPos, true);
ya::renderer::SortByDistance(cutoutList, cameraPos, true);
ya::renderer::SortByDistance(transparentList, cameraPos, false);
//render game objects
ya::renderer::RenderRenderables(opaqueList, viewMatrix, projectionMatrix);
ya::renderer::RenderRenderables(cutoutList, viewMatrix, projectionMatrix);
ya::renderer::RenderRenderables(transparentList, viewMatrix, projectionMatrix);
C++
복사
SceneCamera
void Scene::Render()
{
for (Camera* camera : mCameras)
{
if (camera == nullptr)
continue;
Matrix viewMatrix = camera->GetViewMatrix();
Matrix projectionMatrix = camera->GetProjectionMatrix();
Vector3 cameraPos = camera->GetOwner()->GetComponent<Transform>()->GetPosition();
std::vector<GameObject*> opaqueList = {};
std::vector<GameObject*> cutoutList = {};
std::vector<GameObject*> transparentList = {};
// collect randerables(game objects)
renderer::CollectRenderables(this, opaqueList, cutoutList, transparentList);
// soring renderables by distance (between camera and game object)
renderer::SortByDistance(opaqueList, cameraPos, true);
renderer::SortByDistance(cutoutList, cameraPos, true);
renderer::SortByDistance(transparentList, cameraPos, false);
// render game objects
renderer::RenderRenderables(opaqueList, viewMatrix, projectionMatrix);
renderer::RenderRenderables(cutoutList, viewMatrix, projectionMatrix);
renderer::RenderRenderables(transparentList, viewMatrix, projectionMatrix);
}
}
C++
복사
3. 렌더 파이프라인 호출
씬 뷰에서 다음과 같은 과정을 독립적으로 실행:
씬뷰(타겟에) 물체를 그리기 전에 새로운 뷰행렬을 적용 시키기 위한 기초 작업
렌더타겟을 세팅해주고, 렌더타겟과 뎁스스텐실을 초기화해준다.
void SceneWindow::Run()
{
bool Active = (bool)GetState();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 });
ImGui::Begin(GetName().c_str(), &Active, GetFlag());
Update();
OnGUI();
// Calculate view, projection, and camera position
ya::Transform* cameraTr = mEditorCamera->GetOwner()->GetComponent<ya::Transform>();
cameraTr->LateUpdate();
mEditorCamera->LateUpdate();
// clear the render target view & depth stencil view
ya::graphics::RenderTarget* rt = mEditorCamera->GetRenderTarget();
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> rtv = rt->GetAttachmentTexture(0)->GetRTV();
ya::graphics::GetDevice()->ClearRenderTargetView(rtv);
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> dsv = rt->GetDepthAttachment()->GetDSV();
ya::graphics::GetDevice()->ClearDepthStencilView(dsv);
// set scene view render target & depth stencil view
ya::graphics::GetDevice()->BindRenderTargets(1, rtv.GetAddressOf(), dsv.Get());
....
C++
복사
이 과정을 SceneWindow와 GameWindow에서 각자 따로 수행합니다.
4. ImGui를 통한 최종 출력
•
각각의 뷰는 RenderTarget이 갖는 SRV를 ImGui Image()로 출력
ImGui::Image((ImTextureID)editorCameraRT->GetSRV().Get(), viewportSize);
....
ImGui::Image((ImTextureID)gameRT->GetSRV().Get(), viewportSize);
C++
복사
→ 결국 GPU 버퍼에 그린 결과를 에디터 UI 내에서 이미지처럼 시각화하는 구조
씬뷰와 게임뷰는 각자 다른 카메라, 다른 렌더 타겟, 다른 뷰 행렬을 갖는 완전히 독립된 렌더 파이프라인이며, 최종 결과는 ImGui에서 각각 다른 뷰포트에 출력된다.