오늘은 삼각형을 그려볼 것이다.
삼각형 하나를 그리더라도 전체 파이프라인은 한번 거쳐야한다.
일단 기하학적인 도형을 만들어주는 함수를 만들고 정점을 나타내줄 구조체도 선언해준다.
Stuct.h
#pragma once
#include "Types.h"
struct Vertex
{
Vec3 position; //12바이트 0부터시작
Color color; //12부터시작
};
정점 선언해주기
정점은 -1,1 사이의 범위에서 선언해준다. -> 화면 상의 좌표때문에
정점 정보까지는 아직 CPU에서의 영역,
RAM<->CPU VRAM<->GPU
->VRAM에도 같은 정보를 만들어줘야한다.
-> _device->CreateBuffer(); 마지막인자로 정점버퍼를 받는다 -> 이를 통해 정점버퍼 초기화
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Usage = D3D11_USAGE_IMMUTABLE; //디폴트- GPU만 읽고 쓸수있음
IMMUTABLE GPU만 읽을 수 있다.
DYNAMIC- GPU : 읽기 CPU: 쓰기
STAGING - CPU->GPU 데이터전송
메모리에 있는 정점 넘겨주기 전체코드
이렇게 되면 이제 _vertices가 가지고있던 정점 정보를 gpu쪽으로 넘겨줘서 이후에는 gpu만 read only할 수 있도록 한다.
//정점정보
{
_vertices.resize(3);
_vertices[0].position = Vec3(-0.5f, -0.5f, 0.f);
_vertices[0].color = Color(1.f, 0.f, 0.f, 1.f);
_vertices[1].position = Vec3(0.f, 0.5f, 0.f);
_vertices[1].color = Color(1.f, 0.f, 0.f, 1.f);
_vertices[2].position = Vec3(0.5f, -0.5f, 0.f);
_vertices[2].color = Color(1.f, 0.f, 0.f, 1.f);
}
//정점버퍼
{
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Usage = D3D11_USAGE_IMMUTABLE; //gpu만 read only
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.ByteWidth = (uint32)(sizeof(Vertex) * _vertices.size());
D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(data));
data.pSysMem = _vertices.data(); //첫번째 시작주소 cpu값이 복사된다.
_device->CreateBuffer(&desc,&data, _vertexBuffer.GetAddressOf());
}
이제 입력에 대한 정보를 명시해줘야한다.
void Game::CreateInputLayout()
{
//입력에 대한 정보 ~바이트부터 뛰면 칼러인지
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}
};
const int32 count = sizeof(layout)/sizeof(D3D11_INPUT_ELEMENT_DESC);
_device->CreateInputLayout(layout,count,);
}
하지만 CreateInputLayout 함수의 매개변수에는 쉐이더에 관한 변수가 필요하다.
그럼 쉐이더를 만들어주자
쉐이더 -> 관련 코드를 넣어두는 하나의 파일
필터를 새로만들고 HLSL파일을 만들어보자
쉐이더
-> 1. 먼저 컴파일/ 빌드해서 cso파일을 사용 2.실행 순간에 코드를 읽어서 컴파일 -> 동적으로 사용
->먼저 만들어진걸 사용하는게 좋다. -> 문법적인 /오타 같은 오류를 컴파일 단계에서 알 수 있다.
쉐이더 입력으로 들어오는 구조체부터 선언해주자 이때 InputLayout과 이름을 맞춰주자
struct VS_INPUT
{
float4 position : POSITIONT;
float4 color : COLOR;
};
쉐이더관련 코드를 완성해보자
정점 쉐이더와 픽셀 쉐이더 단계에 필요한 함수를 추가해준다.
struct VS_INPUT
{
float4 position : POSITION;
float4 color : COLOR;
};
struct VS_OUTPUT
{
float4 position : SV_POSITION; //시스템 VALUE 무조건 있어야한다.
float4 color : COLOR;
};
//정점 쉐이더- 위치관련 -> 레스터라이저-정점바탕으로 도형만들고 내/외부 판단 및 보간
VS_OUTPUT VS(VS_INPUT input)
{
VS_OUTPUT output;
output.position = input.position;
output.color = input.color;
return output;
}
//모든 픽셀단위 대상 - 색상관련
float4 PS(VS_OUTPUT input) : SV_Target //랜더 타켓하는 곳으로 보내주기
{
return float4(1, 0, 0, 0);
}
그리고 이제 이 쉐이더를 가져올 함수를 만들자
만들기전에 정점/ 픽셀 쉐이더 관련 변수를 추가해준다.
//VS
ComPtr<ID3D11VertexShader> _vertexShader = nullptr;
ComPtr<ID3DBlob> _vsBlob = nullptr;
//PS
ComPtr<ID3D11PixelShader> _pixelShader = nullptr;
ComPtr<ID3DBlob> _psBlob = nullptr;
void Game::LoadShaderFromFile(const wstring& path, const string& name, const string& version, ComPtr<ID3DBlob>& blob)
{
const uint32 compileFlag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; //디버그 최적화 건너뛰기
HRESULT hr = ::D3DCompileFromFile(
path.c_str(),
nullptr,
D3D_COMPILE_STANDARD_FILE_INCLUDE,
name.c_str(),
version.c_str(),
compileFlag,
0,
blob.GetAddressOf(),
nullptr
);
CHECK(hr);
}
파일에 있던걸 가져와서 어떻게 작동하게 할지 건내줘야해서 가져오는 함수를
VS와 PS에서도 해야하기 때문에 관련 함수를 만들어 준다.
void Game::CreateVS()
{
LoadShaderFromFile(L"Default.hlsl", "VS", "vs_5_0", _vsBlob);
HRESULT hr = _device->CreateVertexShader(_vsBlob->GetBufferPointer(),
_vsBlob->GetBufferSize(), nullptr, _vertexShader.GetAddressOf());
CHECK(hr);
}
void Game::CreatePS()
{
LoadShaderFromFile(L"Default.hlsl", "PS", "ps_5_0", _psBlob);
HRESULT hr = _device->CreatePixelShader(_psBlob->GetBufferPointer(),
_psBlob->GetBufferSize(), nullptr, _pixelShader.GetAddressOf());
CHECK(hr);
}
이제 Init 부분에 만든 함수들을 실행시키도록 넣어준다 .
void Game::Init(HWND hwnd)
{
_hwnd = hwnd;
_width = GWinSizeX;
_height = GwinSizeY;
CreateDeviceAndSwapChain();
CreateRenderTargetView();
SetViewport();
//삼각형 그리기 파트
CreateGeometry();
CreateVS();
CreateInputLayout();
CreatePS();
}
그리고 실제로 그리기 부분은 렌더부분에서 이루어 지기때문에 파이프라인 단계에 맞게 채워준다.
void Game::Render()
{
RenderBegin(); //초기화
// IA - VS - RS - PS -OM
//TODO : 그리기
{
uint32 stride = sizeof(Vertex);
uint32 offset = 0;
//IA - 세팅부분
_deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(),&stride, &offset);
_deviceContext->IASetInputLayout(_inputLayout.Get());
_deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); //삼각형으로 만들어주기
//VS
_deviceContext->VSSetShader(_vertexShader.Get(), nullptr, 0); //이걸로 일하게
//RS
//PS
_deviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0);
//OM
_deviceContext->Draw(_vertices.size(), 0);
}
RenderEnd(); //제출
}
실행화면
만약 PS 단계에서 입력의 색을 따라 만들고 세 점을 RGB 하나씩 가지고 있도록 한다면
//모든 픽셀단위 대상 - 색상관련
float4 PS(VS_OUTPUT input) : SV_Target //랜더 타켓하는 곳으로 보내주기
{
return input.color;
}
이런식으로 그라데이션 삼각형이 나오게 된다.
-> 보간이 잘 이루어졌다.
정점 / 자원 -> cpu만 가지고 있었는데 createVertexBuffer을 통해 gpu로 복사가 이루어진다.
gpu도 해당 자원을 가지고 있게 된다.
IA - createVertexBuffer를 통해 자원을 사용하겠다는 의미
VS- 정점을 gpu에서 연산 처리 -> 정점에 대한 정보가 확정되어서 나가게 된다.
Rasterize- 삼각형있을 때 내/외부 판단, 보간 진행
PS- 모든 픽셀에 대한 색상 - 빛 / 그림자
랜더타켓 - 어디에다 그려줘
VS,PS- 세부적인 옵션을 건드릴 수 있도록하는 툴로 만들면 -> 머테리얼
'게임공부 > Directx11' 카테고리의 다른 글
[Directx11]6. Constant Buffer(상수 버퍼) (0) | 2024.07.09 |
---|---|
[Directx11][C++]5. 텍스처와 UV (0) | 2024.07.01 |
[Directx11]3.장치초기화 (0) | 2024.06.27 |
[Directx11]2.초기설정 (0) | 2024.06.26 |
[Directx11]그래픽스OT (1) | 2024.06.25 |