1. Direct3D는 무엇인가?
로우레벨 그래픽스 API이며 3D 그래픽을 구현해서 화면에 출력할 수 있게끔 os와의 중간 다리 역할을 해준다.
2. COM 객체
- 컴포넌트 오브젝트 모델 (COM)은 독자적으로 그 전 버전들과 호환이 가능한 포인터이며 인터페이스다.
- COM 객체 포인터를 획득하기 위해선 특수 함수로부터 가져와야 한다, new 불가능.
- 다 사용했다면 Release 함수를 통해 제거해준다. delete 하면 뻥 날 수가 있다.
3. 텍스처와 데이터 리소스 확장명
2D 텍스처는 데이터의 행렬이다, 사용 용도 중 하나는 이미지 데이터 용이다. (이미지의 각 픽셀 색상에 대한 정보 저장)
매핑이라는 고급 기술에는 색상 대신 3D 벡터를 저장한다. 그리고 오직 DXGI_FORMAT형만 저장이 가능하다.
4. 스왑 체인이랑 페이지 플리핑
화면의 깜빡이는 현상을 방지하기 위해 제2의 화면에 그 다음 프레임을 미리 그려놓는게 좋다 (백버퍼). 현재 애니메이션이 끝났을 때 그 다음 프레임을 미리 준비해두면 사용자가 변화 과정을 알 필요가 없다. 현재 그리고 있는 화면은 (프론트버퍼)라고 불린다.
5. 깊이 버퍼
텍스처의 이미지 데이터를 가지고 있는게 아닌 특정 픽셀의 깊이 정보를 가지고 있다. 깊이는 0.0부터 1.0까지 가며, 0.0은 사용자와 제일 가깝고, 1.0 제일 멀다.
순서는 다음과 같다.
- 백 버퍼를 백 또는 흑색으로 채운다.
- 기둥 모양부터 깊이 값이 가장 크므로 렌더된다.
- 그 다음 원뿔이 렌더된다.
- 마지막으로 구가 렌더된다.
깊이 버퍼는 텍스처이므로 데이터형 포멧을 아래것 중 사용해야한다.
6. 리소스 뷰에 대한 텍스처의 다른 용도
텍스처는 렌더링 파이프라인 과정 중 아무 곳에 사용이 가능하다. 가장 큰 예시는 렌더 타겟 (Direct3D가 텍스처에 직접 그림)과 셰이더 리소스 (텍스처는 셰이더로 샘플링 된다) 이다.
D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE
리소스 뷰는 Direct3D에게 리소스가 어떤 용도로 사용될 것인지 알려주고 리소스의 대한 타입을 알려준다. 만약에 D3D11_BIND_DEPTH_STENCIL 플래그를 명시 안했다면 ID3D11DepthStencilView로 리소스를 생성할 수가 없다.
모니터의 픽셀들은 매우 작다 따라서 대각선은 정확하게 그릴 수가 없다. 따라서 계단 현상 (앨리어싱) 이펙트가 발생 할 수가 있다. (대각선 그릴 시 다음 그리는 위치에 대한 예측 경로).
해상도를 늘려 픽셀을 더더욱 작게 함으로서 해당 현상을 줄일 수가 있다, 하지만 해상도가 부족하거나 늘리는게 불가능하다면 앤티 에얼리싱 기술을 사용할 수가 있다.
- 슈퍼 샘플링 : 백 버퍼와 깊이 버퍼를 기존보다 4배 크게 할당하는 것이다. 3D 화면은 백버퍼를 보다 더 사용함으로서 해상도를 더 사용할 수가 있다. 하지만 메모리 낭비로 인한 퍼포먼스 저하는 무시할 수가 없다.
- 멀티 샘플링 : 하나의 픽셀을 수십개로 쪼개서 정보를 분할된 여러 픽셀들이 가지고 있게끔 하는 기술이다. 모니터가 렌더링 할 때 중앙 기준으로 한다.
7. Direct3D 멀티 샘플링
기술을 사용하기 위해선 DXGI_SAMPLE_DESC 구조체를 할당 해야한다.
Count 변수는 하나의 픽셀이 가질 샘플링 수를 뜻한다, Quality는 말 그대로 퀄리티이다.
이 함수는 포멧이랑 샘플링 개수 조합이 그래픽 카드와 적합하지가 않다면 0을 반환한다 그게 아니라면 out형 포인터 변수 pNumQualityLevels 통해 전달된다, 0 부터 -1.
따라서 퍼포먼스와 메모리 비용을 적합한 상태에 유지하기 위해선 샘플 개수는 4부터 8가 제일 적합하다. 만약에 멀티 샘플링을 사용하지 않는다면 0으로 설정해야 한다. 모든 Direct3D 11 디바이스들은 4x 멀티 샘플링을 렌더 타깃 포멧과 상관없이 지원한다.
8. Feature Levels (그 전 버전들과 호환성 설정)
Direct3D 11에선 이 프로그램이 어떤 레거시 버전들과 호환이 가능한지 설정할 수가 있다. 9 또는 10. 만약에 호환하지 않는 버전이라면 에러를 뱉는다.
이 배열은 디바이스 초기화 함수에서 설정할 수가 있다.
9. Direct3D 디바이스 초기화
- ID3D11Device (디바이스 인터페이스)와 ID3D11DeviceContext(디바이스 컨텍스트 인터페이스)에 대한 포인터를 D3D11CreateDevice 함수 통해 생성.
- 4x 멀티 샘플링 어느 정도의 퀄리티로 보장되는지 ID3D11Device::CheckMultisampleQualityLevels 함수 통해 확인.
- 스왑 체인을 DXGI_SWAP_CHAIN_DESC 구조체 통해 초기화 한다.
- 사용한 디바이스를 IDXGIFactory형 인스턴스에 할당 후 스왑 체인 IDXGISwapChain 인스턴스를 생성.
- 스왑 체인 백버퍼로 렌더 타깃 뷰를 생성.
- 깊이/스텐실 버퍼와 그에 따른 뷰 생성.
- 렌더 타깃 뷰에 깊이 뷰를 바인딩.
- 뷰 포트 설정
디바이스 생성
UINT createDeviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL featureLevel;
ID3D11Device* md3dDevice;
ID3D11DeviceContext* md3dImmediateContext;
HRESULT hr = D3D11CreateDevice(
0, // default adapter
D3D_DRIVER_TYPE_HARDWARE,
0, // no software device
createDeviceFlags,
0, 0, // default feature level array
D3D11_SDK_VERSION,
& md3dDevice,
& featureLevel,
& md3dImmediateContext);
if( FAILED(hr) )
{
MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);
return false;
}
if( featureLevel != D3D_FEATURE_LEVEL_11_0 )
{
MessageBox(0, L"Direct3D Feature Level 11 unsupported.", 0, 0);
return false;
}
4x 멀티 샘플링 퀄리티 체크
스왑 체인 설정
DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mClientWidth; // use window's client area dims
sd.BufferDesc.Height = mClientHeight;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
// Use 4X MSAA?if( mEnable4xMsaa )
{
sd.SampleDesc.Count = 4;
// m4xMsaaQuality is returned via CheckMultisampleQualityLevels().
sd.SampleDesc.Quality = m4xMsaaQuality-1;
}// No MSAA
else
{
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
}
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = mhMainWnd;
sd.Windowed = true;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags = 0;
스왑 체인 생성
IDXGIDevice* dxgiDevice = 0;
HR(md3dDevice->QueryInterface(__uuidof(IDXGIDevice),
(void**)&dxgiDevice));
IDXGIAdapter* dxgiAdapter = 0;
HR(dxgiDevice->GetParent(__uuidof(IDXGIAdapter),
(void**))&dxgiAdapter));
// Finally got the IDXGIFactory interface.
IDXGIFactory* dxgiFactory = 0;
HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory),(void**))&dxgiFactory));
// Now, create the swap chain.
IDXGISwapChain* mSwapChain;
HR(dxgiFactory->CreateSwapChain(md3dDevice, )&sd, )&mSwapChain));
// Release our acquired COM interfaces (because we are done with them).
ReleaseCOM(dxgiDevice);
ReleaseCOM(dxgiAdapter);
ReleaseCOM(dxgiFactory);
렌더 타깃 뷰 생성
깊이/스텐실 버퍼와 뷰 생성
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
// Use 4X MSAA? --must match swap chain MSAA values.
if( mEnable4xMsaa )
{
depthStencilDesc.SampleDesc.Count = 4;
depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality-1;
}// No MSAA
else
{
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
}
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
ID3D11Texture2D* mDepthStencilBuffer;
ID3D11DepthStencilView* mDepthStencilView;
HR(md3dDevice->CreateTexture2D(
&depthStencilDesc, // Description of texture to create.
0,
&mDepthStencilBuffer)); // Return pointer to depth/stencil buffer.
HR(md3dDevice->CreateDepthStencilView(
mDepthStencilBuffer, // Resource we want to create a view to.
0,
&mDepthStencilView)); // Return depth/stencil view
렌더 타깃에 뷰 바인딩
뷰포트 설정
D3D11_VIEWPORT vp;vp.TopLeftX = 0.0f;
vp.TopLeftY = 0.0f;
vp.Width = static_cast<float>(mClientWidth);
vp.Height = static_cast<float>(mClientHeight);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
md3dImmediateContext->RSSetViewports(1, &vp);
타이밍과 애니메이션
애니메이션을 정교하게 실행하기 위해선 현재 시간을 재야한다. 이를 재기 위해선 #include <Windows.h> 성능 타이머(또는 카운터) (Performance Timer / Counter) https://novemberfirst.tistory.com/36 참고.
현재 타이머를 재는 1번째 방법. 게임 타이머 클래스 생성
class GameTimer
{
public:
GameTimer();
float GameTime()const; // in seconds
float DeltaTime()const; // in seconds
void Reset(); // Call before message loop.
void Start(); // Call when unpaused.
void Stop(); // Call when paused.
void Tick(); // Call every frame.
private:
double mSecondsPerCount;
double mDeltaTime;
__int64 mBaseTime;
__int64 mPausedTime;
__int64 mStopTime;
__int64 mPrevTime;
__int64 mCurrTime;
bool mStopped;
};
GameTimer::GameTimer()
: mSecondsPerCount(0.0), mDeltaTime(-1.0), mBaseTime(0),
mPausedTime(0), mPrevTime(0), mCurrTime(0), mStopped(false)
{
__int64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
mSecondsPerCount = 1.0 / (double)countsPerSec;
}
void GameTimer::Tick()
{
if (mStopped)
{
mDeltaTime = 0.0;
return;
}// Get the time this frame.
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mCurrTime = currTime;
// Time difference between this frame and the previous.
mDeltaTime = (mCurrTime - mPrevTime) * mSecondsPerCount;
// Prepare for next frame.
mPrevTime = mCurrTime;
// Force nonnegative. The DXSDK's CDXUTTimer mentions that if the
// processor goes into a power save mode or we get shuffled to another
// processor, then mDeltaTime can be negative.
if (mDeltaTime < 0.0)
{
mDeltaTime = 0.0;
}
}
float GameTimer::DeltaTime()const
{
return (float)mDeltaTime;
}
void GameTimer::Reset()
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
mBaseTime = currTime;
mPrevTime = currTime;
mStopTime = 0;
mStopped = false;
}
아래는 틱 함수 사용 예제다
int D3DApp::Run()
{
MSG msg = {0};
mTimer.Reset();
while(msg.message != WM_QUIT)
{
// If there are Window messages then process them.
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Otherwise, do animation/game stuff.
else
{
mTimer.Tick();
if(!mAppPaused)
{
CalculateFrameStats();
UpdateScene(mTimer.DeltaTime());
DrawScene();
}
else
{
Sleep(100);
}
}
}
return (int)msg.wParam;
}
현재 타이머를 재는 2번째 방법.
총 걸리는 시간 바탕으로 재면 된다.
void GameTimer::Stop()
{
// If we are already stopped, then don't do anything.
if (!mStopped)
{
__int64 currTime;
QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
// Otherwise, save the time we stopped at, and set
// the Boolean flag indicating the timer is stopped.
mStopTime = currTime;
mStopped = true;
}
}
void GameTimer::Start()
{
__int64 startTime;
QueryPerformanceCounter((LARGE_INTEGER*)&startTime);
// Accumulate the time elapsed between stop and start pairs.
//
// |<-------d------->|
// ---------------*-----------------*------------> time
// mStopTime startTime// If we are resuming the timer from a stopped state...
if (mStopped)
{
// then accumulate the paused time.
mPausedTime += (startTime - mStopTime);
// since we are starting the timer back up, the current
// previous time is not valid, as it occurred while paused.
// So reset it to the current time.
mPrevTime = startTime;
// no longer stopped...
mStopTime = 0;
mStopped = false;
}
}
float GameTimer::TotalTime()const
{
// If we are stopped, do not count the time that has passed
// since we stopped. Moreover, if we previously already had
// a pause, the distance mStopTime - mBaseTime includes paused
// time,which we do not want to count. To correct this, we can
// subtract the paused time from mStopTime:
//
// previous paused time
// |<----------->|
// ---*------------*-------------*-------*-----------*------> time
// mBaseTime mStopTime mCurrTime
if (mStopped)
{
return (float)(((mStopTime - mPausedTime) -
mBaseTime) * mSecondsPerCount);
}
// The distance mCurrTime - mBaseTime includes paused time,
// which we do not want to count. To correct this, we can subtract
// the paused time from mCurrTime:
//
// (mCurrTime - mPausedTime) - mBaseTime
//
// |<--paused time-->|
// ----*---------------*-----------------*------------*------> time
// mBaseTime mStopTime startTime mCurrTime
else
{
return (float)(((mCurrTime - mPausedTime) -
mBaseTime) * mSecondsPerCount);
}
}
'그래픽스 > DirectX' 카테고리의 다른 글
[DX11 물방울책] 챕터 5 - 렌더링 파이프라인 (0) | 2022.07.03 |
---|---|
DirectX 11 프레임워크 환경 설정하는 방법 (0) | 2022.06.30 |
[DX11 물방울책] 챕터3 - 트랜스폼 (위치값,회전값,크기값) (0) | 2022.06.23 |
[DX11 물방울책] 챕터 2 - 행렬 (0) | 2022.06.23 |
[DX11 물방울책] 챕터 1 - 벡터 (0) | 2022.06.22 |