튀김족발
공부 끄적이는 공간
튀김족발
전체 방문자
오늘
어제
  • 분류 전체보기 (33)
    • C++ (10)
    • DirectX (12)
    • Unreal (11)
    • Unity (0)
    • EASTL (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
튀김족발

공부 끄적이는 공간

3. 버퍼, 쉐이더 및 HLSL
DirectX

3. 버퍼, 쉐이더 및 HLSL

2022. 10. 10. 12:49

 버텍스 버퍼(Vertex Buffer)

이해해야 할 첫 번째 개념은 정점 버퍼 입니다. 이 개념을 설명하기 위해 구의 3D 모델을 예로 들어보겠습니ㅏㄷ.

3D 구형 모델은 실제로 수백개의 삼각형으로 구성됩니다.

 

구형 모델의 각 삼각형에는 3개읜 점이 있으며, 각 점을 꼭짓점이라고 부릅니다. 따라서 우리가 구형 모델을 렌더링 하려면, 구형을 구성하는 모든 정점을 정점버퍼라고 부르는 특수 데이터 배열에 넣어야 합니다. 구형 모델의 모든 점이 버텍스 버퍼에 있으면 우리는 버텍스 버퍼를 GPU 에 전송하여 모델을 렌더링 할 수 있습니다.

인덱스 버퍼

인덱스 버퍼는 정점 버퍼와 관련이 있습니다. 그 목적은 정점 버퍼에 있는 각 정점의 위치를 기록하는 것입니다. 그런 다음 GPU는 인덱스 버퍼를 사용하여 정점 버퍼의 특정 정점을 빠르게 찾습니다. 색인 버퍼의 개념은 책에서 색인을 사용하는 개념과 유사하며, 찾고있는 주제를 훨씬 더 빠르게 찾을 수 있습니다. DirectX 문서에서는 인덱스 버퍼를 사용하면, 비디오 메모리의 더 빠른 위치에 정점 데이터를 캐싱 할 가능성을 높일 수 있다고 합니다. 따라서 성능상의 이유로도 인덱스 버퍼 사용을 권장합니다.

버텍스 쉐이더

정점 쉐이더(버텍스 쉐이더)는 주로 정점 버퍼의 정점들을 3D 공간으로 변환시켜주는 작은 프로그램입니다. 이 외에도 각 정점의 법선을 계산하던가 하는 다른 연산도 가능합니다. 정점 쉐이더 프로그램은 GPU 에서 계산이 필요하다고 판단될 때 호출됩니다. 예를 들어, 5,000개의 폴리곤을 가진 모델을 화면에 표시한다면, 단지 저 하나의 모델을 드리기 위해 매 프레임미다 15000번의 정점 쉐이더 프로그램이 실행됩니다. 따라서 프로그램이 60fps의 fps를 가진 그래픽 프로그램에서는 단지 5000개의 삼각형을 그리기 위해 매 초마다 900,000번의 정점 쉐이더를 호출하게 됩니다. 따라서 효율적인 정점 쉐이더를 작성하는 것이 중요합니다.

픽셀 쉐이더

픽셀 쉐이더는 그리고자 하는 도형에 색상을 입힐 때를 위한 작은 프로그램입니다. 이것은 화며에 보여지는 모든 픽셀들에 대해 GPU에서 연산됩니다. 색상을 입히고(coloring), 텍스쳐를 입히고(texturing), 광원 효과를 주고(lighting), 그 외 다른 많은 도형 채색 효과를 주는 것이 바로 픽셀 쉐이더 프로그램에서 제어됩니다. 픽셀 쉐이더는 GPU에 의해 수 없이 호출되기 때문에 반드시 효율적으로 작성되어야 합니다.

HLSL

HLSL은 앞서 설명한 DirectX11에서 사용하는 작은 정점 및 픽셀 쉐이더 프로그램을 작성할 때 사용하는 일종의 언어입니다. 구문은 미리 정의된 타입이 있나는 것을 빼면 C언어와 거의 동일합니다. HLSL 프로그램 파일은 전역 변수, 타입 정의, 정점 쉐이더, 픽셀 쉐이더, 그리고 기하 쉐이더(Geometry Shader)로 구성되어 있습니다. 이 튜토리얼은 HLSL을 사용하는 첫번째이므로 우선 DirectX 11을 이용하여 아주 간단 한 HLSL 프로그램을 실행해 볼 것입니다.

 

프레임워크 업데이트

이 튜토리얼을 위해 프레임워크가 한층 업데이트 되었습니다. GraphicsClass 안에 CameraClass, ModelClass , ColorShaderClass라고 하는 세 클래스를 추가하였습니다. CameraClass는 지난번에 이야기했던 뷰 행렬을 다룰 것입니다. 이것은 현재 월드에서 카메라의 윛, 보는 방향을 제어하며, 이 위치를 필요한 쉐이더에 전달합니다. ModelClass는 3D 객체들의 기하학적인 부분을 다룰 것인데, 이 튜토리얼에서는 단순함을 유지하기 위해 3D 객체를 삼각형으로 할 것입니다. 마지막으로 ColorShaderClass는 직접 작성한 HLSL 쉐이더를 호출하여 객체들을 그리는 일을 맡게 될 것입니다.

 

쉐이더는 실데 모델의 렌더링을 수행하는 작은 프로그램입니다. 이 쉐이더는 color.vs 와 color.ps라는 소스파일에 HLSL로 작성되었습니다. 이 쉐이더의 목적은 첫 HLSL튜토리얼이기 때문에 가능한 한 단순함을 유지하면서 색상이 있는 삼각형을 그리고자 하는것입니다. 우선 정점 쉐이더 먼저 보겠습니다.

Color.vs

/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
	matrix worldMatrix;
	matrix viewMatrix;
	matrix projectionMatrix;
}

//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
	float4 position : POSITION;
	float4 color : COLOR;
};
struct PixelInputType
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

////////////////////
// Vertext Shader //
////////////////////
PixelInputType ColorVertexShader(VertexInputType input)
{
	PixelInputType output;

	// 적절한 행렬 계산을 위해 위치 벡터를 4 단위로 변경합니다.
	input.position.w = 1.0f;

	// 월드, 뷰 및 투영행렬에 대한 정점의 위치를 계산합니다.
	output.position = mul(input.position, worldMatrix);
	output.position = mul(output.position, viewMatrix);
	output.position = mul(output.position, projectionMatrix);
	
	// 픽셀 쉐이더가 사용할 입력 색상을 저장합니다.
	output.color = input.color;

	return output;
}

픽셀 쉐이더는 화면에 그려지는 도형의 각 픽셀들을 실제로 그립니다. 이 픽셀 쉐이더에서는 PixelInputType을 입력으로 사용하고, 최종 픽셀의 색상이 저장된 float4 타입을 반환합니다. 여기서 소개하는 픽셀 쉐이더는 단지 입력 생강을 바로 출력으로 내보내는 단순한 프로그램입니다. 정점쉐이더의 결과물을 픽셀 쉐이더에서 사용합니다.

Color.ps

/////////////
// GLOBALS //
/////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};


//////////////////
// Pixel Shader //
//////////////////
float4 ColorPixelShader(PixelInputType input) : SV_TARGET
{
	return input.color;
}

다음은 ColorShaderClass에 대해 살펴보겠습니다. ColorShaderClass는 Gpu에 있는 3D모델을 그리기 위해 HLSL 쉐이더를 호출하는데 사용할 것 입니다.

ColorShaderClass.h

#pragma once

class ColorShaderClass : public AlignedAllocationPolicy<16>
{
private:
	struct MatrixBufferType
	{
		XMMATRIX world;
		XMMATRIX view;
		XMMATRIX projection;
	};

public:
	ColorShaderClass();
	ColorShaderClass(const ColorShaderClass& colorShaderClass);
	~ColorShaderClass();

	bool Initialize(ID3D11Device* device, HWND hwnd);
	void ShutDown();
	bool Render(ID3D11DeviceContext* deviceContext, int inputCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix);

private:
	bool InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFileName, WCHAR* psFileName);
	void ShutdownShader();
	void OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFileName);

	bool SetShaderParameters(ID3D11DeviceContext* deviceContext, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix);
	void RenderShader(ID3D11DeviceContext* deviceContext, int indexCount);

private:
	ID3D11VertexShader* _vertexShader = nullptr;
	ID3D11PixelShader* _pixelShader = nullptr;
	ID3D11InputLayout* _layout = nullptr;
	ID3D11Buffer* _matrixBuffer = nullptr;
};

ColorShaderClass.cpp

#include "stdafx.h"
#include "ColorShaderClass.h"

ColorShaderClass::ColorShaderClass()
{
}

ColorShaderClass::ColorShaderClass(const ColorShaderClass& colorShaderClass)
{
}

ColorShaderClass::~ColorShaderClass()
{
}

bool ColorShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
    // 정점 및 픽셀 쉐이더를 초기화합니다.
    return InitializeShader(device, hwnd, L"Shader/color.vs", L"Shader/color.ps");
}

void ColorShaderClass::ShutDown()
{
    // 버텍스 및 픽셀 쉐이더 관련된 객체를 종료합니다.
    ShutdownShader();
}

bool ColorShaderClass::Render(ID3D11DeviceContext* deviceContext, int inputCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix)
{
    // 렌더링에 사용할 쉐이더 매개 변수를 설정합니다.
    if (SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix) == false)
    {
        return false;
    }
    
    // 설정된 버퍼를 쉐이더로 렌더링 합니다.
    RenderShader(deviceContext, inputCount);

    return true;
}

bool ColorShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFileName, WCHAR* psFileName)
{
    ID3D10Blob* errorMessage = nullptr;

    // 버텍스 쉐이더 코드를 컴파일합니다.
    ID3D10Blob* vertexShaderBuffer = nullptr;
    if (FAILED(D3DCompileFromFile(vsFileName, NULL, NULL, "ColorVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &vertexShaderBuffer, &errorMessage)))
    {
        // 쉐이더 컴파일 실패시 오류 메시지를 출력합니다.
        if (errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, vsFileName);
        }
        // 컴파일 오류가 아니라면 쉐이더 파일을 찾을 수 없는 경우 입니다.
        else
        {
            MessageBox(hwnd, vsFileName, L"Missing Shader File", MB_OK);
        }
        return false;
    }

    // 픽셀 쉐이더 코드를 컴파일한다.
    ID3D10Blob* pixelShaderBuffer = nullptr;
    if (FAILED(D3DCompileFromFile(psFileName, NULL, NULL, "ColorPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &pixelShaderBuffer, &errorMessage)))
    {
        // 쉐이더 컴파일 실패시 오류 메시지를 출력합니다.
        if (errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, psFileName);
        }
        // 컴파일 오류가 아니라면 쉐이더 파일을 찾을 수 없는 경우 입니다.
        else
        {
            MessageBox(hwnd, psFileName, L"Missing Shader File", MB_OK);
        }
        return false;
    }

    // 버퍼로부터 정점 쉐이더를 생성합니다.
    if (FAILED(device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &_vertexShader)))
    {
        return false;
    }

    if (FAILED(device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &_pixelShader)))
    {
        return false;
    }

    // 정점 입력 레이아웃 구조체를 설정합니다.
    // 이 설정은 ModelCalss와 쉐이더 VertexType 구조와 일치해야 합니다.
    D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
    polygonLayout[0].SemanticName = "POSITION";
    polygonLayout[0].SemanticIndex = 0;
    polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    polygonLayout[0].InputSlot = 0;
    polygonLayout[0].AlignedByteOffset = 0;
    polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    polygonLayout[0].InstanceDataStepRate = 0;

    polygonLayout[1].SemanticName = "COLOR";
    polygonLayout[1].SemanticIndex = 0;
    polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    polygonLayout[1].InputSlot = 0;
    polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
    polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    polygonLayout[1].InstanceDataStepRate = 0;

    // 레이아웃의 요소 수를 가져옵니다.
    unsigned int numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

    // 정점 입력 레이아웃을 만듭니다.
    if (FAILED(device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &_layout)))
    {
        return false;
    }

    // 더 이상 사용되지 않는 정점 쉐이더 버퍼와 픽쉘 쉐이더 버퍼를 해제합니다.
    vertexShaderBuffer->Release();
    vertexShaderBuffer = 0;

    pixelShaderBuffer->Release();
    pixelShaderBuffer = 0;

    // 정점 쉐이더에 있는 행렬 상수 버퍼의 구조체를 작성합니다.
    D3D11_BUFFER_DESC matrixBufferDesc;
    matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
    matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    matrixBufferDesc.MiscFlags = 0;
    matrixBufferDesc.StructureByteStride = 0;

    // 상수 버퍼 포인터를 만들어 이 클래스에서 정점 쉐이더 상수 버퍼에 접근할 수 있게 합니다.
    if (FAILED(device->CreateBuffer(&matrixBufferDesc, NULL, &_matrixBuffer)))
    {
        return false;
    }

    return true;
}

void ColorShaderClass::ShutdownShader()
{
    // 행렬 상수 버퍼를 해제합니다.
    if(_matrixBuffer)
    {
        _matrixBuffer->Release();
        _matrixBuffer = 0;
    }

    // 레이아웃을 해제 합니다
    if (_layout)
    {
        _layout->Release();
        _layout = 0;
    }

    // 픽셀 쉐이더를 해제합니다.
    if (_pixelShader)
    {
        _pixelShader->Release();
        _pixelShader = 0;
    }

    // 버텍스 쉐이더를 해제합니다.
    if (_vertexShader)
    {
        _vertexShader->Release();
        _vertexShader = 0;
    }
}

void ColorShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFileName)
{
    // 에러 메시지를 출력창에 표시합니다.
    OutputDebugStringA(reinterpret_cast<const char*>(errorMessage->GetBufferPointer()));

    // 에러 메시지를 반환합니다.
    errorMessage->Release();
    errorMessage = 0;

    // 컴파일 에러가 있음을 팝업 메세지로 알려줍니다.
    MessageBox(hwnd, L"Error Compiling Shader", shaderFileName, MB_OK);
}

bool ColorShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix)
{
    // 행렬을 transpose하여 쉐이더에서 사용할 수 있게 합니다.
    worldMatrix = XMMatrixTranspose(worldMatrix);
    viewMatrix = XMMatrixTranspose(viewMatrix);
    projectionMatrix = XMMatrixTranspose(projectionMatrix);

    // 상수 버퍼의 내용을 쓸 수 있도록 잠급니다.
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    if (FAILED(deviceContext->Map(_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
    {
        return false;
    }

    // 상수 버퍼으 ㅣ데이터에 대한 포인터를 가져옵니다.
    MatrixBufferType* dataPtr = (MatrixBufferType*)mappedResource.pData;

    // 상수 버퍼에 행렬을 복사합니다.
    dataPtr->world = worldMatrix;
    dataPtr->view = viewMatrix;
    dataPtr->projection = projectionMatrix;

    // 상수 버퍼의 잠금을 풉니다.
    deviceContext->Unmap(_matrixBuffer, 0);

    // 정점 쉐이더에서 상수 버퍼의 위치를 설정합니다.
    unsigned bufferNumber = 0;

    // 마지막으로 정점 쉐이더의 상수 버퍼를 바뀐 값으로 바꿉니다.
    deviceContext->VSSetConstantBuffers(bufferNumber, 1, &_matrixBuffer);

    return true;
}

void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
    // 정점 입력 레이아웃을 설정합니다.
    deviceContext->IASetInputLayout(_layout);

    // 삼각형을 그릴 정점 쉐이더와 픽셀 쉐이더를 설정합니다.
    deviceContext->VSSetShader(_vertexShader, NULL, 0);
    deviceContext->PSSetShader(_pixelShader, NULL, 0);

    // 삼각형을 그립니다.
    deviceContext->DrawIndexed(indexCount, 0, 0);
}

앞서 언급했듯이, modelClass는 3D 모델들의 복잡한 모양들을 캡슐화 하는 클래스입니다. 이 튜토리얼에서는 단일 녹색 삼각형을 만들기 위한 데이터를 차근차근 만들어 볼 것입니다. 또한 이 삼각형이 화면에 그려지기 위해 필요한 정점 버퍼와 인덱스 버퍼도 역시 만들 것입니다.

ModelClass.h

#pragma once

class ModelClass : public AlignedAllocationPolicy<16>
{
private:
	struct VertexType
	{
		XMFLOAT3 position;
		XMFLOAT4 color;
	};

public:
	ModelClass();
	ModelClass(const ModelClass& modelClass);
	~ModelClass();

	bool Initialize(ID3D11Device* device);
	void ShutDown();
	void Render(ID3D11DeviceContext* deviceContext);

	int GetIndexCount();

private:
	bool InitializeBuffers(ID3D11Device* device);
	void ShutdownBuffers();
	void RenderBuffers(ID3D11DeviceContext* deviceContext);

private:
	ID3D11Buffer* _vertexBuffer = nullptr;
	ID3D11Buffer* _indexBuffer = nullptr;
	int _vertexCount = 0;
	int _indexCount = 0;
};

ModelClass.cpp

#include "stdafx.h"
#include "ModelClass.h"

ModelClass::ModelClass()
{
}

ModelClass::ModelClass(const ModelClass& modelClass)
{
}

ModelClass::~ModelClass()
{
}

bool ModelClass::Initialize(ID3D11Device* device)
{
    // 정점 및 인덱스 버퍼를 초기화 합니다.
    return InitializeBuffers(device);
}

void ModelClass::ShutDown()
{
    // 버텍스 및 인덱스 버퍼를 종료합니다.
    ShutdownBuffers();
}

void ModelClass::Render(ID3D11DeviceContext* deviceContext)
{
    // 그리기를 준비하기 위해 그래픽 파이프 라인에 꼭지점과 인덱스 버퍼를 놓습니다.
    RenderBuffers(deviceContext);
}

int ModelClass::GetIndexCount()
{
    return _indexCount;
}

bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
    // 정점 배열의 정점 수를 설정합니다.
    _vertexCount = 3;

    // 인덱스 배열의 인덱스 수를 설정합니다.
    _indexCount = 3;
    
    // 정점 배열을 만듭니다.
    VertexType* vertices = new VertexType[_vertexCount];
    if (vertices == nullptr)
    {
        return false;
    }

    // 인ㄷㄱ스 배열을 만듭니다.
    unsigned long* indices = new unsigned long[_indexCount];
    if (indices == nullptr)
    {
        return false;
    }

    // 정점 배열에 데이터를 설정합니다.
    vertices[0].position = XMFLOAT3(-1.0f, -1.0f, 0.0f);    // Botton Left
    vertices[0].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);

    vertices[1].position = XMFLOAT3(0.0f, 1.0f, 0.0f);    // Top Middle
    vertices[1].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);

    vertices[2].position = XMFLOAT3(1.0f, -1.0f, 0.0f);    // Botton Right
    vertices[2].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);

    // 인덱스 배열의 값을 설정합니다.
    indices[0] = 0; // Botton Left
    indices[1] = 1; // Top Middle
    indices[2] = 2; // Botton Right

    // 정적 정점 버퍼의 구조체를 설정합니다.
    D3D11_BUFFER_DESC vertexBufferDesc;
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * _vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

    // Subresource 구조에 정점 데이터에 대한 포인터를 제공합니다.
    D3D11_SUBRESOURCE_DATA vertexData;
    vertexData.pSysMem = vertices;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;

    // 이제 정점 버퍼를 만듭니다.
    if (FAILED(device->CreateBuffer(&vertexBufferDesc, &vertexData, &_vertexBuffer)))
    {
        return false;
    }

    // 정점 인덱스 버퍼의 구조체를 설정합니다.
    D3D11_BUFFER_DESC indexBufferDesc;
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * _indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;

    // 인덱스 데이터를 가르키는 보조 리소스 구조체를 작성합니다.
    D3D11_SUBRESOURCE_DATA indexData;
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;

    // 인덱스 버퍼를 생성합니다.
    if (FAILED(device->CreateBuffer(&indexBufferDesc, &indexData, &_indexBuffer)))
    {
        return false;
    }

    // 생성되고 값이 할당된 정점 버퍼와 인덱스 버퍼를 해제합니다.
    delete[] vertices;
    vertices = 0;

    delete[] indices;
    indices = 0;

    return true;
}

void ModelClass::ShutdownBuffers()
{
    // 인덱스 버퍼를 해제합니다.
    if (_indexBuffer)
    {
        _indexBuffer->Release();
        _indexBuffer = 0;
    }

    // 정점 버퍼를 해제합니다.
    if (_vertexBuffer)
    {
        _vertexBuffer->Release();
        _vertexBuffer = 0;
    }
}

void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
    // 정점 버퍼의 단위와 오프셋을 설정합니다.
    unsigned int stride = sizeof(VertexType);
    unsigned int offset = 0;

    // 렌더링 할 수 있도록 입력 어셈들러에서 정점 버퍼를 활성으로 설정합니다.
    deviceContext->IASetVertexBuffers(0, 1, &_vertexBuffer, &stride, &offset);

    // 렌더링 할 수 있도록 입력 어셈블러에서 인덱스 버퍼를 활성으로 설정합니다.
    deviceContext->IASetIndexBuffer(_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

    // 정점 버퍼로 그릴 기본형을 설정합니다. 여기서는 삼각형으로 설정합니다.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

지금까지 우리는 HLSL의 프로그래밍과, 정점/인덱스 버퍼를 셋업하는 방법 그리고 ColorShaderClass를 사용하여 HLSL 쉐이더를호출하는 방법을 살펴보았습니다. 하지만, 우리가 한가지 놓친 것이 있습니다. 그것은 바로 월드에서 우리가 보는 시점입니다. 이를 구현하기 위해서는 어떻게 우리가 장면을 보는지 대한 정보를 DirectX11 에게 전달하는 카메라 클래스가 필요합니다. 카메라 클래스는 카메라의 위치와 현재 회전 상태를 계속 가지고 있어야 합니다. 또한 이 정보를 이요하여 렌더링 시에 HLSL 쉐이더에서 사용할 뷰 행렬을 생성합니다.

CameraClass.h

#pragma once

class CameraClass : public AlignedAllocationPolicy<16>
{
public:
	CameraClass();
	CameraClass(const CameraClass& cameraClass);
	~CameraClass();

	void SetPosition(float x, float y, float z);
	void SetRotation(float x, float y, float z);

	XMFLOAT3 GetPosition();
	XMFLOAT3 GetRotation();

	void Render();
	void GetViewMatrix(XMMATRIX& viewMatrix);

private:
	XMFLOAT3 _position;
	XMFLOAT3 _rotation;
	XMMATRIX _viewMatrix;
};

CameraClass의 헤더는 단지 4개의 함수만을 사용하는 간단한 구조입니다. SetPosition과 SetRotation함수는 현재 카메라 객체의 위치와 회전 상태를 설정하는 데 사용될 것입니다. Render함수는 카메라의 위치와 회전 상태에 기반한 뷰 행렬을 생성하는 데 사용됩니다. 그리고 마지막으로 GetViewMatrix 함수는 쉐이더에서 렌더링에 사용할 수 있도록 카레라 객체의 뷰 행렬을 받아오는 데 사용합니다.

CameraClass.cpp

#include "stdafx.h"
#include "CameraClass.h"

CameraClass::CameraClass()
{
	_position = XMFLOAT3(0.0f, 0.0f, 0.0f);
	_rotation = XMFLOAT3(0.0f, 0.0f, 0.0f);
	_viewMatrix = XMMATRIX();
}

CameraClass::CameraClass(const CameraClass& cameraClass)
{
	
}

CameraClass::~CameraClass()
{
}

void CameraClass::SetPosition(float x, float y, float z)
{
	_position.x = x;
	_position.y = y;
	_position.z = z;
}

void CameraClass::SetRotation(float x, float y, float z)
{
	_rotation.x = x;
	_rotation.y = y;
	_rotation.z = z;
}

XMFLOAT3 CameraClass::GetPosition()
{
	return _position;
}

XMFLOAT3 CameraClass::GetRotation()
{
	return _rotation;
}

void CameraClass::Render()
{
	XMFLOAT3 up, position, lookAt;
	XMVECTOR upVector, positionVector, lookAtVector;
	float yaw, pitch, roll;
	XMMATRIX rotationMatrix;

	// 위쪽을 가리키는 벡터를 설정합니다.
	up.x = 0.0f;
	up.y = 1.0f;
	up.z = 0.0f;

	// XMVECTOR 구조체에 로드한다.
	upVector = XMLoadFloat3(&up);

	// 3D 월드에서 카메라의 위치를 설정합니다.
	position = _position;

	// XMVECTOR 구조체에 로드한다.
	positionVector = XMLoadFloat3(&position);

	// 기본적으로 카메라가 찾고있는 위치를 설정합니다.
	lookAt.x = 0.0f;
	lookAt.y = 0.0f;
	lookAt.z = 1.0f;

	// XMVECTOR 구조체에 로드한다.
	lookAtVector = XMLoadFloat3(&lookAt);

	// yaw (Y 축), pitch (X 축), roll (Z 축)의 회전값을 라디안 단위로 설정합니다.
	pitch = _rotation.x * 0.0173432925f;
	yaw = _rotation.y * 0.0173432925f;
	roll = _rotation.z * 0.0173432925f;

	// yaw, pitch, roll 값을 통해 회전 행렬을 만듭니다.
	rotationMatrix = XMMatrixRotationRollPitchYaw(pitch, yaw, roll);

	// lookAt 및 up 벡터를 회전 행렬로 변형하여 뷰가 원점에서 올바르게 회전되도록 합니다.
	lookAtVector = XMVector3TransformCoord(lookAtVector, rotationMatrix);
	upVector = XMVector3TransformCoord(upVector, rotationMatrix);

	// 회전 된 카메라의 위치를 뷰어 위치로 변환합니다.
	lookAtVector = XMVectorAdd(positionVector, lookAtVector);

	// 마지막으로 세 개의 업데이트 된 벡터에서 뷰 행렬을 만듭니다.
	_viewMatrix = XMMatrixLookAtLH(positionVector, lookAtVector, upVector);
}

void CameraClass::GetViewMatrix(XMMATRIX& viewMatrix)
{
	viewMatrix = _viewMatrix;
}

GraphicsClass는 이제 세 개의 새로운 클래스가 추가되었습니다. 따라서 CameraClass, ModelClass, ColorShader 클래스의 헤더뿐만 아니라 멤버 변수가 추가됩니다. GraphicsClass가 이 프로젝트에서 사용되는 모든 그래픽 객체에 대한 호출을 담당한다는 점을 기억하시기 바랍니다.

GraphicClass.h

#pragma once

/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;

class D3DClass;
class CameraClass;
class ModelClass;
class ColorShaderClass;

class GraphicsClass
{
public:
	GraphicsClass();
	GraphicsClass(const GraphicsClass& other);
	~GraphicsClass();

	bool Initialize(int screenWidth, int screenHeight, HWND hwnd);
	void Shutdown();
	bool Frame();

private:
	bool Render();

private:
	D3DClass* _direct3D = nullptr;
	CameraClass* _camera = nullptr;
	ModelClass* _model = nullptr;
	ColorShaderClass* _colorShader = nullptr;
};

GraphicsClass.cpp

#include "stdafx.h"
#include "d3dclass.h"
#include "CameraClass.h"
#include "ModelClass.h"
#include "ColorShaderClass.h"
#include "GraphicsClass.h"


GraphicsClass::GraphicsClass()
{
}


GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}


GraphicsClass::~GraphicsClass()
{
}


bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
	// Direct3D 객체 생성
	_direct3D = (D3DClass*) SAFE_ALIGNED_NEW(sizeof(D3DClass), 16);
	if (!_direct3D)
	{
		return false;
	}

	// Direct3D 객체 초기화
	if (!_direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR))
	{
		MessageBox(hwnd, L"Could not initialze Direct3D", L"Error", MB_OK);
		return false;
	}

	// _camera 객체 생성
	_camera = new CameraClass();
	if (_camera == nullptr)
	{
		return false;
	}

	// 카메라 포지션 변경
	_camera->SetPosition(0.0f, 0.0f, -5.0f);

	// _model 객체 생성
	_model = new ModelClass();
	if (_model == nullptr)
	{
		return false;
	}

	// _model 객체 초기화
	if (!_model->Initialize(_direct3D->GetDevice()))
	{
		MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
		return false;
	}

	// _colorShader 생성
	_colorShader = new ColorShaderClass();
	if (_colorShader == nullptr)
	{
		return false;
	}

	// _colorShader 객체 초기화
	if (!_colorShader->Initialize(_direct3D->GetDevice(), hwnd))
	{
		MessageBox(hwnd, L"Could not initialize the color shader object.", L"Error", MB_OK);
		return false;
	}

	return true;
}


void GraphicsClass::Shutdown()
{
	if (_colorShader)
	{
		_colorShader->ShutDown();
		SAFE_DELETE(_colorShader);
	}

	if (_model)
	{
		_model->ShutDown();
		SAFE_DELETE(_model);
	}

	// _camera 객체 반환
	SAFE_DELETE(_camera);

	// Direct3D 객체 반환
	if (_direct3D)
	{
		_direct3D->Shutdown();
		SAFE_ALIGNED_DELETE(_direct3D);
	}
}


bool GraphicsClass::Frame()
{
	// 그래픽 랜더링 처리
	return Render();
}


bool GraphicsClass::Render()
{
	// 씬을 그리기 위해 버퍼를 지웁니다.
	_direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

	// 카메라의 위치에 따라 뷰 행렬을 생성합니다.
	_camera->Render();

	// 카메라 및 d3d 객체에서 월드, 뷰 및 투영 행렬을 가져옵니다.
	XMMATRIX worldMatrix, viewMatrix, projectionMatrix;
	_direct3D->GetWorldMatrix(worldMatrix);
	_camera->GetViewMatrix(viewMatrix);
	_direct3D->GetProjectionMatrix(projectionMatrix);

	// 모델 버텍스와 인덱스 버퍼를 그래픽 파이프 라인에 배치하여 드로잉을 준비합니다.
	_model->Render(_direct3D->GetDeviceContext());

	// 색상 쉐이더를 사용하여 모델을 렌더링 합니다.
	if (!_colorShader->Render(_direct3D->GetDeviceContext(), _model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix))
	{
		return false;
	}
	
	// 버퍼의 내용을 화면에 출력합니다.
	_direct3D->EndScene();

	return true;
}

 

출력화면

'DirectX' 카테고리의 다른 글

5. 조명  (0) 2022.11.21
4. 텍스쳐  (0) 2022.11.11
번외) Warning - warning C4316 에러 문제  (0) 2022.10.03
2. DirectX 초기화  (0) 2022.10.03
1. 프레임 워크 및 윈도우  (0) 2022.10.03
    'DirectX' 카테고리의 다른 글
    • 5. 조명
    • 4. 텍스쳐
    • 번외) Warning - warning C4316 에러 문제
    • 2. DirectX 초기화
    튀김족발
    튀김족발

    티스토리툴바