이번 단계에서는 씬을 별도 텍스처(오프스크린 RT)에 렌더링하고, 그 텍스처를 ImGui의 Viewport 패널 안에 이미지로 표시합니다.
프레임 흐름
[매 프레임]
├─ (패널 크기 변경 요청 시) WaitIdle → RT Resize
├─ ImGui NewFrame + UI 구성 ← Viewport 패널 크기를 읽고 ImGui::Image(SRV) 기록
├─ Renderer::BeginFrame ← 백버퍼 PRESENT → RT, Clear
├─ ViewportRT.Begin ← 텍스처 PSR → RT, Clear(color/depth), Viewport/Scissor
│ └─ SceneRenderer::Render ← 큐브 Draw
├─ ViewportRT.End ← 텍스처 RT → PSR (셰이더에서 읽을 수 있게)
├─ Renderer::BindBackBuffer ← 다시 백버퍼를 타깃으로
├─ ImGui Render ← Viewport 패널이 RT 텍스처를 샘플링
└─ Renderer::EndFrame ← RT → PRESENT, Execute, Present
핵심은 같은 텍스처가 한 프레임 안에서 두 가지 역할을 한다는 점입니다.
씬 패스에서는 RenderTarget으로 쓰이고, UI패스에서는 ShaderResource로 읽힙니다.
그래서 두 패스 사이에 리소스 배리어(RT<->PSR)가 반드시 필요합니다.
RenderTarget (Engine/RHI/RenderTarget)
| 항목 | 내용 |
| 소유 리소스 | 컬러 텍스처(R8G8B8A8_UNORM), 깊이 텍스처(D32_FLOAT) |
| 디스크립터 | RTV(RTV힙), DSV(DSV힙), SRV(shader-visible CBV_SRV_UAV 힙) |
| 기본 상태 | 컬러는 PIXEL_SHADER_RESOURCE, 깊이는 DEPTH_WRITE |
| Init | 디스크립터를 한 번만 Allocate해 두고 재사용 |
| Resize(w, h) | 텍스처 재생성 후 같은 슬롯에 RTV/DSV/SRV를 다시 기록 |
| Begin / End | 배리어, 바인딩, Clear,Viewport / Scissor 설정 |
Optimized Clear Value: 리소스 생성 시 D3D12_CLEAR_VALUE에 넣은 색과 실제 Clear 색을 맞춰야 경고가 나지 않고 Fast Clear 최적화가 적용됩니다.
리사이즈 시 동기화: GPU가 아직 사용 중인 텍스처를 해제하면 안 되므로, 현재는 WaitIdle 후 재 생성합니다.
Renderer 확장
RTV힙(64개)과 DSV힙(16개)을 추가했습니다. 둘 다 non-shader-visible입니다.
BindBackBuffer()는 씬 패스가 끝난 뒤 다시 백버퍼를 렌더 타킷으로 지정합니다.
SceneRenderer (Engine/Renderer/SceneRenderer)
루트 시그니처: Root Constants 16개 (b0=MVP행렬)만 사용합니다(가장 단순한 형태).
PSO: 기본 Rasterizer/Blend/Depth, RTV는 R8G8B8A8, DSV는 D32입니다.
지오메트리: 큐브 8개 정점에 정점 색을 넣고, 인덱스 36개(R16_UINT)를 씁니다.
MVP: Wolrd(회전) * LookAtLH * PerspectiveFovLH를 Transpose해서 전달합니다.aspect는 RT 크기 기준으로 계산합니다.
Shader 컴파일 헬퍼 (Engine/RHI/Shader)
D3DCompileFromFile을 래핑했습니다. Debug 빌드에서는 DEBUG | SKIP_OPTIMIZATION 을 켭니다.
에러 메시지를 OutputDebugStringA로 출력 창에 남기고, 예외로도 던집니다.
셰이더 경로는 CMake에서 BAEK_SHADER_DIR="${CMAKE_SOURCE_DIR}/Shaders/" 로 주입합니다. 이렇게 하면 실행 위치(working dir)와 무관하게 셰이더를 찾을 수 있습니다.
에디터 UI
Viewport 패널: 패딩을 0으로 두고, GetContentRegionAvail()로 얻은 크기를 다음 프레임 RT 크기로 요청합니다. 표시는 ImGui::Image(SRV GPU handle)로 합니다.
DockBuilder 기본 레이아웃: imgui.ini가 없을 때만 Viewport를 가운데, Status를 오른쪽 22%에 배치합니다.이후 사용자가 바꾼 배치는 ini에 저장돼 유지됩니다.
트러블 슈팅
Basic.hlsl(1,1): error X3000: Illegal character -> .editorconfig가 hlsl을 UTF-8 BOM으로 저장. FXC는 BOM을 처리하지 못함
해결(.editorconfig으로 hlsl만 utf-8로 저장)
root = true
[*.{h,hpp,cpp,txt,json}]
charset = utf-8-bom
[*.hlsl]
charset = utf-8
결과

'DirectX12 > 엔진 만들어보기' 카테고리의 다른 글
| 라이팅에서 PBR 머티리얼까지 (0) | 2026.10.06 |
|---|---|
| 그리드, Scene/Entity, Hierarchy·Inspector, 기즈모 (0) | 2026.10.05 |
| 서브모듈 정리, 카메라 (0) | 2026.10.03 |
| 엔진 구상 (0) | 2026.10.01 |
