이 문서는 SimpleMath 강의를 기반으로 정리한 완전 복습·학습 패키지입니다. 핵심 개념, 코드 구조, 파이프라인 설명, 실습, 퀴즈, 디버깅 가이드까지 한 번에 복습할 수 있도록 구성했습니다.
World → View → Projection을 거쳐 Clip Space로 변환하고, 뷰포트 매핑 후 화면에 렌더링.TransformData { matWorld, matView, matProjection }cbuffer → VS에서 mul()로 WVP 계산파이프라인: IA → VS → RS → PS → OM (Input Assembler → Vertex Shader → Rasterizer → Pixel Shader → Output Merger)
using Vec2 = DirectX::SimpleMath::Vector2;
using Vec3 = DirectX::SimpleMath::Vector3;
using Vec4 = DirectX::SimpleMath::Vector4;
using Matrix = DirectX::SimpleMath::Matrix;
struct TransformData
{
Matrix matWorld = Matrix::Identity;
Matrix matView = Matrix::Identity;
Matrix matProjection = Matrix::Identity;
};
Vec3 _localPosition = { 0.f, 0.f, 0.f };
Vec3 _localRotation = { 0.f, 0.f, 0.f };
Vec3 _localScale = { 1.f, 1.f, 1.f };
Transform과 동일한 개념Update()에서 SRT 행렬을 구성해 matWorld로 반영Matrix matScale = Matrix::CreateScale(_localScale);
Matrix matRotation = Matrix::CreateRotationX(_localRotation.x);
matRotation *= Matrix::CreateRotationY(_localRotation.y);
matRotation *= Matrix::CreateRotationZ(_localRotation.z);
Matrix matTranslation = Matrix::CreateTranslation(_localPosition);
Matrix matWorld = matScale * matRotation * matTranslation; // S * R * T (중요!)
_transformData.matWorld = matWorld;
중요: S → R → T 순서를 바꾸면 전혀 다른 결과가 나옵니다.
D3D11_MAPPED_SUBRESOURCE subResource = {};
_deviceContext->Map(_constantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
::memcpy(subResource.pData, &_transformData, sizeof(_transformData));
_deviceContext->Unmap(_constantBuffer.Get(), 0);
D3D11_MAP_WRITE_DISCARD로 매 프레임 쓰기memcpy로 Struct 전체를 그대로 복사 → 간단·안전D3D11_BUFFER_DESC desc = {};
desc.Usage = D3D11_USAGE_DYNAMIC; // CPU write + GPU read
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.ByteWidth = sizeof(TransformData);
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
_device->CreateBuffer(&desc, nullptr, _constantBuffer.GetAddressOf());
cbuffer TransformData : register(b0)
{
row_major matrix matWorld;
row_major matrix matView;
row_major matrix matProjection;
}
struct VS_INPUT { float4 position : POSITION; float2 uv : TEXCOORD; };
struct VS_OUTPUT { float4 position : SV_POSITION; float2 uv : TEXCOORD; };
VS_OUTPUT VS(VS_INPUT input)
{
VS_OUTPUT o;
float4 p = mul(input.position, matWorld); // W
p = mul(p, matView); // V
p = mul(p, matProjection);// P
o.position = p;
o.uv = input.uv;
return o;
}
mul(vector, matrix) 순서에 유의Texture2D texture0 : register(t0);
SamplerState sampler0 : register(s0);
float4 PS(VS_OUTPUT input) : SV_Target
{
return texture0.Sample(sampler0, input.uv);
}
TransformData(b0) 바인딩, WVP 곱디버깅 시 단계별 상태가 원하는 대로 세팅되어 있는지 순서대로 점검하세요.
World의 역행렬을 사용하는 패턴을 기억_localScale = Lerp(_localScale, target, dt);_localRotation.y += 0.5f * dt;_localPosition.x += 0.1f * dt;matView = Matrix::CreateLookAt(...), matProjection = Matrix::CreatePerspectiveFieldOfView(...)Viewport.Unproject() 경로 탐색 및 Ray 생성row_major 누락 → 변환 왜곡/전치 발생sizeof(TransformData) 사용, 16바이트 정렬 보장D3DCompileFromFile()의 프로파일/엔트리명 확인1) 왜 SRT 순서가 중요할까요?
2) VS에서 W → V → P 순서로 곱하는 이유는?
3) row_major를 쓰지 않으면 어떤 문제가 생기나요?
4) LH 프로젝트에서 SimpleMath(RH 가정)를 쓸 때 무엇을 주의해야 하나요?
5) Constant Buffer를 매 프레임 업데이트할 때 안전한 Map 플래그는?
정답 예시: (1) 비가환 연산이라 결과가 달라짐 (2) 로컬→월드→카메라→클립 순서이기 때문 (3) 전치/왜곡 (4) Look 축 부호/뷰 행렬 생성 시 부호 (5)
D3D11_MAP_WRITE_DISCARD
Matrix::CreateScale/RotationX/Y/Z/TranslationMatrix::IdentityViewport.Project / UnprojectD3D11_BUFFER_DESC (Usage/Bind/CPUAccess)D3DCompileFromFile (엔트리/프로파일/플래그)CreateLookAt, CreatePerspectiveFieldOfViewcbuffer 레이아웃과 C++ struct의 멤버 순서/크기 일치 여부// Create
D3D11_BUFFER_DESC desc = {};
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.ByteWidth = sizeof(TransformData);
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
_device->CreateBuffer(&desc, nullptr, _constantBuffer.GetAddressOf());
// Update
D3D11_MAPPED_SUBRESOURCE subResource = {};
_deviceContext->Map(_constantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
::memcpy(subResource.pData, &_transformData, sizeof(_transformData));
_deviceContext->Unmap(_constantBuffer.Get(), 0);
struct VS_INPUT { float4 position : POSITION; float2 uv : TEXCOORD; };
struct VS_OUTPUT { float4 position : SV_POSITION; float2 uv : TEXCOORD; };
cbuffer TransformData : register(b0)
{
row_major matrix matWorld;
row_major matrix matView;
row_major matrix matProjection;
}
Texture2D texture0 : register(t0);
SamplerState sampler0 : register(s0);
VS_OUTPUT VS(VS_INPUT input)
{
VS_OUTPUT o;
float4 p = mul(input.position, matWorld);
p = mul(p, matView);
p = mul(p, matProjection);
o.position = p;
o.uv = input.uv;
return o;
}
float4 PS(VS_OUTPUT input) : SV_Target
{
return texture0.Sample(sampler0, input.uv);
}
_localScale /= 3.f; // 더 작게
_localPosition.x += 0.001f; // 우측으로 이동
이 가이드를 기준으로 SRT 구성 → World 작성 → ConstantBuffer 업데이트 → VS에서 WVP 적용의 전체 흐름을 반복 학습하세요. 확장 과제(카메라/투영/다중 오브젝트)까지 진행하면, 렌더링 파이프라인의 핵심을 완성하게 됩니다. 화이팅!
좋아요. 강의 블로그 글 + 코드 + 녹음 텍스트를 한데 묶어 완전 복습용 학습 파일로 정리했어요. 먼저 바로 쓰실 수 있게 마크다운 학습 노트를 만들어 두었습니다:
아래에는 그 노트의 핵심을 요약하고, 각 항목이 파일에서 나온 근거를 함께 달아 두었습니다.
World → View → Projection 을 거쳐 Clip Space로 보낸 뒤 뷰포트에 그리기. VS에서 이 WVP 곱을 수행합니다. IA → VS → RS → PS → OM 순서로 생각하면 점검이 쉬워요. Vec2/Vec3/Vec4/Matrix → DirectX::SimpleMath 타입. matWorld / matView / matProjection 3개 행렬을 항등으로 초기화해 두고 매 프레임 갱신합니다. D3D11_USAGE_DYNAMIC + D3D11_CPU_ACCESS_WRITE, 크기는 sizeof(TransformData). CreateScale → CreateRotationX/Y/Z → CreateTranslation 생성 후 matWorld = S * R * T. (순서 중요!) Map(DISCARD) → memcpy → Unmap 패턴으로 TransformData를 상수 버퍼에 복사합니다. b0로 정의, 특히 row_major를 명시해 CPU와 행렬 메모리 정렬을 맞춥니다. mul(input.position, matWorld) → matView → matProjection 순서로 누적해 최종 위치를 구합니다. Texture2D.Sample()로 색을 가져옵니다. Viewport는 Project/Unproject를 제공하므로, 피킹(스크린→월드) 등 역변환 실습에도 바로 활용 가능합니다. D3DCompileFromFile(path, ..., entryName, profile, flags, ...) 시그니처와 엔트리/프로파일(예: VS, vs_5_0 등)을 정확히 맞춰야 합니다. Vec2/Vec3/Vec4/Matrix 별칭 선언했는가? Update()에서 S * R * T 순서로 matWorld를 만들었는가? cbuffer에 row_major와 동일 멤버 순서로 선언했는가? S*R*T 및 VS에서 W→V→P 순서를 재확인. row_major가 빠졌는지 확인. D3DCompileFromFile 엔트리/프로파일/플래그 재확인. 필요하면 이 노트에 문제/퀴즈, 과제, 체크리스트도 더 확장해 드릴 수 있어요. 우선 위 파일로 복습해 보시고, 보강하고 싶은 파트를 알려 주세요!