DirectX11 Tutorial 35: 깊이 버퍼

강좌번역/DirectX 11 2018. 5. 12. 14:52 by 빠재

원문: Tutorial 35: Depth Buffer

DirectX 11에서 깊이 버퍼(또는 뎁스 버퍼, Z버퍼)는 주로 현재 프러스텀에서 모든 픽셀의 깊이값을 기록하는 데 사용합니다. 같은 위치에 여러 픽셀이 자리하는 경우 깊이값을 이용하여 어느 픽셀을 사용할 지 고르게 됩니다. 하지만 깊이 버퍼의 용도는 그것 하나로 한정되지 않고 다른 여러 용도로도 사용할 수 있습니다.

대부분의 그래픽 카드들은 32비트 깊이 버퍼를 가지고 있기 때문에 이 32비트로 무엇을 할 것인지는 사용하기에 달려 있습니다. D3DClass::Initialize 함수를 보면 깊이 버퍼의 포맷이 DXGI_FORMAT_D24_UNORM_S8_UNIT으로 되어 있습니다. 이것은 깊이 버퍼를 스텐실 버퍼로도 사용하는데 24비트를 깊이 채널에 사용하고 나머지 8비트를 스텐실 채널에 사용한다는 내용입니다. 또한 깊이 버퍼가 어떻게 작동하는지도 정의할 수 있습니다. 우리의 현재 설정에서는 현재 픽셀보다 멀리 있는 것은 사용하지 않게 되어 있습니다. 하지만 이 설정을 바꾸어 원하는 효과를 내는 데 사용할 수 있습니다. 버퍼의 동작을 완전히 제어할 수 있는 것입니다.

깊이 버퍼의 값들은 부동소수점입니다. 그 범위는 가까운 클리핑 평면이 0.0f으로 시작하여 먼 클리핑 평면이 1.0f으로 끝나게 됩니다. 하지만 깊이 버퍼에 기록된 값들의 분포는 선형적이지(linear) 않습니다. 대략 90%의 값들 범위는 가까운 클리핑 평면에 있는 처음 10%의 영역을 차지합니다. 나머지 10%(0.9f ~ 1.0f)의 값들이 깊이 버퍼의 나머지 90%를 차지합니다. 아래 다이어그램은 값들의 분포를 보여주는데 검은 영역은 0.0f이고 흰색이 1.0f입니다. 제가 절반쯤 위치에 0.5표시를 했지만 그부분도 99.999%가 흰색이어서 저정도 거리에서는 정밀도가 떨어지는 것을 알게 되실 겁니다.

3D 장면의 관점에서 보고 싶다면 아래 그림을 보시기 바랍니다:

저 물체들의 깊이값들을 색상으로 바꾸어 그려보면 가까이 있는 육면체는 적절한 색상 범위로 나오지만 구체는 디테일이 거의 보이지 않는 완전한 하얀색으로 보입니다.

깊이 버퍼가 저렇게 설정되는 이유는 대부분의 어플리케이션들에서 가까운 물체에 대해서는 상당한 정확도를 요구하지만 멀리 있는 물체에 대해서는 비교적 중요하지 않기 때문입니다. 만약 여러분의 어플리케이션에서는 거리가 중요하다면 그 어플리케이션에서는 멀리 같이 있는 물체들은 겹치는 부분의 픽셀이 깜빡이는 문제가 생길 수 있습니다(이를 Z fighting이라고도 합니다). 여러 해결 방법들이 있지만 구현 방법에 따라 어느 정도 희생해야 할 것들이 있습니다.

이번 예제에서는 간단히 10단위짜리 평면을 그리고 깊이 정보에 따라 색상을 다르게 할 것입니다. 화면에 가장 가까운 부분은 붉은색으로, 그 다음 부분은 녹색으로, 남은 먼 곳의 부분은 파란색으로 칠할 것입니다. 이렇게 하는 이유는 깊이가 어떻게 계산되는지 자세히 확인하는 방법을 보여주기 위한 것입니다. 이 방식을 이용하여 가까울 때에는 범프 매핑을 사용하고 멀리 있을 때에는 일반 디퓨즈 라이팅을 사용하는 식의 확장도 가능합니다. 또한 이를 더욱 다양한 용도로 사용하여 섀도우 매핑에 쓰는 사례도 있으나, 우선 기본부터 시작해 보도록 하겠습니다.

프레임워크

이 예제의 프레임워크는 깊이 렌더링을 담당하는 DepthShaderClass이 추가되었습니다.

HLSL 깊이 셰이더부터 코드 소개를 시작하겠습니다.

Depth.vs

////////////////////////////////////////////////////////////////////////////////
// Filename: depth.vs
////////////////////////////////////////////////////////////////////////////////

깊이 정점 셰이더는 행렬 버퍼만 필요하며, 다른 전역 변수들은 필요하지 않습니다.

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


//////////////
// TYPEDEFS //
//////////////

정점 셰이더에서는 위치만 필요로 합니다. 깊이값으로만 색칠을 할 것이기 때문에 텍스쳐링, 노말 등 다른 것들은 필요하지 않습니다.

struct VertexInputType
{
    float4 position : POSITION;
};

픽셀 셰이더의 입력으로는 이전과 같이 클리핑 공간에 있는 정점의 위치를 position 변수에 저장하여 사용합니다. 하지만 SV_POSITION 타입은 위치를 0.5만큼 이동시켜놓기 때문에 깊이 계산을 하기 위해서는 수정되지 않은 두 번째 좌표인 depthPosition이 필요합니다.

struct PixelInputType
{
    float4 position : SV_POSITION;
    float4 depthPosition : TEXTURE0;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType DepthVertexShader(VertexInputType input)
{
    PixelInputType output;
    
    // 적절한 행렬 연산을 위해 position 벡터의 성분이 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.depthPosition = output.position;
    
    return output;
}

Depth.ps

////////////////////////////////////////////////////////////////////////////////
// Filename: depth.ps
////////////////////////////////////////////////////////////////////////////////


//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float4 depthPosition : TEXTURE0;
};


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 DepthPixelShader(PixelInputType input) : SV_TARGET
{
    float depthValue;
    float4 color;

우선 이 픽셀의 깊이값을 가져옵니다. 참고로 깊이값은 z/w의 형태로 저장되어 있기 때문에 이 연산을 수행해야 합니다.

    // Get the depth value of the pixel by dividing the Z pixel depth by the homogeneous W coordinate.
    depthValue = input.depthPosition.z / input.depthPosition.w;

다음 부분에서는 픽셀의 색상을 정합니다. 예제 맨 위에 있던 다이어그램에서 처음 10%의 깊이 버퍼가 90%의 부동소수점 범위를 어떻게 처리하는지 기억하시기 바랍니다. 이 예제에서는 그 90% 범위를 빨간색으로 칠할 것입니다. 그리고 다음 아주 작은 부분은 녹색으로 칠하는데, 정확히는 0.025%의 영역이지만 near평면과 가까이 있기 때문에 화면상으로는 넓은 영역을 차지합니다. 정확도가 얼마나 빨리 떨어지는지 이해하는데 이 녹색 영역이 도움이 될 겁니다. 그리고 나머지 부분은 파란색으로 칠합니다.

    // 처음 10% 범위는 빨간색
    if(depthValue < 0.9f)
    {
        color = float4(1.0, 0.0f, 0.0f, 1.0f);
    }
    
    // 다음 0.025% 범위는 녹색
    if(depthValue > 0.9f)
    {
        color = float4(0.0, 1.0f, 0.0f, 1.0f);
    }

    // 나머지 깊이 버퍼는 파란색
    if(depthValue > 0.925f)
    {
        color = float4(0.0, 0.0f, 1.0f, 1.0f);
    }

    return color;
}

Depthshaderclass.h

DepthShaderClass는 첫 HLSL 예제에서 처음 색상 셰이더를 작성했을 때 봤던 간단한 셰이더 클래스와 비슷합니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: depthshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _DEPTHSHADERCLASS_H_
#define _DEPTHSHADERCLASS_H_


//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <d3dx11async.h>
#include <fstream>
using namespace std;


////////////////////////////////////////////////////////////////////////////////
// Class name: DepthShaderClass
////////////////////////////////////////////////////////////////////////////////
class DepthShaderClass
{
private:
    struct MatrixBufferType
    {
        D3DXMATRIX world;
        D3DXMATRIX view;
        D3DXMATRIX projection;
    };

public:
    DepthShaderClass();
    DepthShaderClass(const DepthShaderClass&);
    ~DepthShaderClass();

    bool Initialize(ID3D11Device*, HWND);
    void Shutdown();
    bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX);

private:
    bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*);
    void ShutdownShader();
    void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);

    bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX);
    void RenderShader(ID3D11DeviceContext*, int);

private:
    ID3D11VertexShader* m_vertexShader;
    ID3D11PixelShader* m_pixelShader;
    ID3D11InputLayout* m_layout;

여기서는 행렬을 위한 상수 버퍼만 써도 됩니다.

    ID3D11Buffer* m_matrixBuffer;
};

#endif

Depthshaderclass.cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: depthshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "depthshaderclass.h"

생성자에서 모든 private 포인터들을 null로 초기화합니다.

DepthShaderClass::DepthShaderClass()
{
    m_vertexShader = 0;
    m_pixelShader = 0;
    m_layout = 0;
    m_matrixBuffer = 0;
}


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


DepthShaderClass::~DepthShaderClass()
{
}


bool DepthShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
    bool result;

depth.fx HLSL 셰이더를 불러옵니다.

    // 정점 및 픽셀 셰이더를 초기화합니다.
    result = InitializeShader(device, hwnd, L"../Engine/depth.vs", L"../Engine/depth.ps");
    if(!result)
    {
        return false;
    }

    return true;
}


void DepthShaderClass::Shutdown()
{
    // 연관된 객체들과 함께 정점 및 픽셀 셰이더를 정리합니다.
    ShutdownShader();

    return;
}

다시 한 번 말하지만 이 셰이더는 정말 간단하기 때문에 3개의 기본 행렬만 입력으로 받습니다.

bool DepthShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, 
                  D3DXMATRIX projectionMatrix)
{
    bool result;


    // 렌더링에 사용할 셰이더 파라미터들을 설정합니다.
    result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix);
    if(!result)
    {
        return false;
    }

    // 셰이더를 이용하여 준비된 버퍼에 그립니다.
    RenderShader(deviceContext, indexCount);

    return true;
}


bool DepthShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
    HRESULT result;
    ID3D10Blob* errorMessage;
    ID3D10Blob* vertexShaderBuffer;
    ID3D10Blob* pixelShaderBuffer;

polygonLayout 디스크립션은 하나만 있어도 됩니다.

    D3D11_INPUT_ELEMENT_DESC polygonLayout[1];
    unsigned int numElements;
    D3D11_BUFFER_DESC matrixBufferDesc;


    // Initialize the pointers this function will use to null.
    errorMessage = 0;
    vertexShaderBuffer = 0;
    pixelShaderBuffer = 0;

깊이 정점 셰이더를 불러옵니다.

    // 정점 셰이더를 컴파일합니다.
    result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "DepthVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
                       &vertexShaderBuffer, &errorMessage, NULL);
    if(FAILED(result))
    {
        // If the shader failed to compile it should have writen something to the error message.
        if(errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
        }
        // If there was nothing in the error message then it simply could not find the shader file itself.
        else
        {
            MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
        }


        return false;
    }

깊이 픽셀 셰이더를 불러옵니다.

    // 픽셀 셰이더를 컴파일합니다.
    result = D3DX11CompileFromFile(psFilename, NULL, NULL, "DepthPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
                       &pixelShaderBuffer, &errorMessage, NULL);
    if(FAILED(result))
    {
        // If the shader failed to compile it should have writen something to the error message.
        if(errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
        }
        // If there was nothing in the error message then it simply could not find the file itself.
        else
        {
            MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
        }

        return false;
    }

    // 버퍼에서 정점 셰이더를 생성합니다.
    result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader);
    if(FAILED(result))
    {
        return false;
    }

    // 정점에서 픽셀 셰이더를 생성합니다.
    result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader);
    if(FAILED(result))
    {
        return false;
    }

참고로 HLSL 셰이더의 입력에 맞추기 위해 레이아웃에는 위치만 설정해줍니다. ModelClassVertexType 역시 이에 맞추어 수정되어 있어야 합니다.

    // 정점 입력 레이아웃 디스크립션을 생성합니다.
    // 이 설정은 ModelClass::VertexType과 셰이더의 입력과 일치해야 합니다.
    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;

    // 레이아웃 배열의 크기를 구합니다.
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

    // 정접 입력 레이아웃을 생성합니다.
    result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout);
    if(FAILED(result))
    {
        return false;
    }

    // 더이상 사용하지 않는 정점 및 픽셀 셰이더 버퍼를 해제합니다.
    vertexShaderBuffer->Release();
    vertexShaderBuffer = 0;

    pixelShaderBuffer->Release();
    pixelShaderBuffer = 0;

    // 정점 셰이더에 있는 동적 행렬 상수 버퍼의 디스크립션을 설정합니다.
    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;

    // 이 클래스에서 정점 셰이더 상수 버퍼에 접근하기 위하여 상수 버퍼 포인터를 생성합니다.
    result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
    if(FAILED(result))
    {
        return false;
    }

    return true;
}


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

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

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

    // 정점 셰이더를 해제합니다.
    if(m_vertexShader)
    {
        m_vertexShader->Release();
        m_vertexShader = 0;
    }

    return;
}


void DepthShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename)
{
    char* compileErrors;
    unsigned long bufferSize, i;
    ofstream fout;


    // Get a pointer to the error message text buffer.
    compileErrors = (char*)(errorMessage->GetBufferPointer());

    // Get the length of the message.
    bufferSize = errorMessage->GetBufferSize();

    // Open a file to write the error message to.
    fout.open("shader-error.txt");

    // Write out the error message.
    for(i=0; i<bufferSize; i++)
    {
        fout << compileErrors[i];
    }

    // Close the file.
    fout.close();

    // Release the error message.
    errorMessage->Release();
    errorMessage = 0;

    // Pop a message up on the screen to notify the user to check the text file for compile errors.
    MessageBox(hwnd, L"Error compiling shader.  Check shader-error.txt for message.", shaderFilename, MB_OK);

    return;
}

셰이더의 전역 입력으로 세 개의 기본 행렬만 있으면 됩니다.

bool DepthShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
    HRESULT result;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    unsigned int bufferNumber;
    MatrixBufferType* dataPtr;


    // 셰이더에 필요한 행렬들을 변환해 놓습니다.
    D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
    D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
    D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);

    // 상수 버퍼에 쓰기 위하여 락을 겁니다.
    result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // 상수 버퍼의 포인터를 얻어옵니다.
    dataPtr = (MatrixBufferType*)mappedResource.pData;

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

    // 상수 버퍼의 락을 해제합니다.
    deviceContext->Unmap(m_matrixBuffer, 0);
    
    // 정점 셰이더에서의 상수 버퍼의 위치를 설정합니다.
    bufferNumber = 0;

    // 정점 셰이더에 갱신된 상수 버퍼를 설정합니다.
    deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);

    return true;
}


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

    // 삼각형을 그리이 위한 정점 및 픽셀 셰이더를 설정합니다.
    deviceContext->VSSetShader(m_vertexShader, NULL, 0);
    deviceContext->PSSetShader(m_pixelShader, NULL, 0);

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

    return;
}

Graphicsclass.h

////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_


///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"

DepthShaderClass를 위한 헤더를 추가합니다.

#include "depthshaderclass.h"


/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = true;
const bool VSYNC_ENABLED = true;

near 평면과 far평면의 값을 바꾸어 더 큰 범위에서 정확도가 소실되지 않도록 합니다.

const float SCREEN_DEPTH = 100.0f;
const float SCREEN_NEAR = 1.0f;


////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
    GraphicsClass();
    GraphicsClass(const GraphicsClass&);
    ~GraphicsClass();

    bool Initialize(int, int, HWND);
    void Shutdown();
    bool Frame();

private:
    bool Render();

private:
    D3DClass* m_D3D;
    CameraClass* m_Camera;
    ModelClass* m_Model;

DepthShaderClass 객체를 추가합니다.

    DepthShaderClass* m_DepthShader;
};

#endif

Graphicsclass.cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"


GraphicsClass::GraphicsClass()
{
    m_D3D = 0;
    m_Camera = 0;
    m_Model = 0;

생성자에서 DepthShaderClass 객체를 null로 초기화합니다.

    m_DepthShader = 0;
}


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


GraphicsClass::~GraphicsClass()
{
}


bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
    bool result;


    // Direct3D 객체를 생성합니다.
    m_D3D = new D3DClass;
    if(!m_D3D)
    {
        return false;
    }

    // Direct3D 객체를 초기화합니다.
    result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
        return false;
    }

    // 카메라를 생성합니다.
    m_Camera = new CameraClass;
    if(!m_Camera)
    {
        return false;
    }

카메라를 조금 올려 렌더링될 평면을 보게 합니다.

    // 카메라의 처음 위치를 설정합니다.
    m_Camera->SetPosition(0.0f, 2.0f, -10.0f);
    
    // 모델 객체를 생성합니다.
    m_Model = new ModelClass;
    if(!m_Model)
    {
        return false;
    }

floor.txt 모델을 불러옵니다.

    // 모델 객체를 초기화합니다.
    result = m_Model->Initialize(m_D3D->GetDevice(), "../Engine/data/floor.txt");
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
        return false;
    }

    // 깊이 셰이더 객체를 생성합니다.
    m_DepthShader = new DepthShaderClass;
    if(!m_DepthShader)
    {
        return false;
    }

    // 깊이 셰이더 객체를 초기화합니다.
    result = m_DepthShader->Initialize(m_D3D->GetDevice(), hwnd);
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize the depth shader object.", L"Error", MB_OK);
        return false;
    }

    return true;
}


void GraphicsClass::Shutdown()
{

Shutdown 함수에서 DepthShaderClass를 해제합니다.

    // 깊이 셰이더 객체를 해제합니다.
    if(m_DepthShader)
    {
        m_DepthShader->Shutdown();
        delete m_DepthShader;
        m_DepthShader = 0;
    }

    // 모델 객체를 해제합니다.
    if(m_Model)
    {
        m_Model->Shutdown();
        delete m_Model;
        m_Model = 0;
    }

    // 카메라 객체를 해제합니다.
    if(m_Camera)
    {
        delete m_Camera;
        m_Camera = 0;
    }

    // D3D 객체를 해제합니다.
    if(m_D3D)
    {
        m_D3D->Shutdown();
        delete m_D3D;
        m_D3D = 0;
    }

    return;
}


bool GraphicsClass::Frame()
{
    bool result;


    // 그래픽 화면을 그립니다.
    result = Render();
    if(!result)
    {
        return false;
    }

    return true;
}


bool GraphicsClass::Render()
{
    D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
    bool result;


    // 시작하기 위하여 화면을 지웁니다.
    m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

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

    // 카메라와 D3D 객체에서 월드, 뷰, 투영 행렬을 가져옵니다.
    m_Camera->GetViewMatrix(viewMatrix);
    m_D3D->GetWorldMatrix(worldMatrix);
    m_D3D->GetProjectionMatrix(projectionMatrix);

    // 그릴 준비를 하기 위하여 모델의 정점 및 인덱스 버퍼를 파이프라인에 넣어줍니다.
    m_Model->Render(m_D3D->GetDeviceContext());

바닥 모델을 깊이 셰이더를 이용하여 그립니다.

    // 깊이 셰이더를 이용하여 모델을 그립니다.
    result = m_DepthShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix);
    if(!result)
    {
        return false;
    }

    // 그려진 화면을 스크린에 표시합니다.
    m_D3D->EndScene();

    return true;
}

마치면서

이제 깊이 범위를 표현하는 색상들로 바닥을 그리게 되었습니다. 깊이 버퍼는 또 다른 많은 용도로도 사용된다는 걸 기억하시기 바랍니다.

연습 문제

  1. 프로그램을 다시 컴파일해보십시오. 세 가지 깊이 색상으로 칠해진 바닥이 보일 것입니다.
  2. 픽셀 셰이더의 사용된 범위를 바꾸어 어떻게 출력이 바뀌는지 확인해 보십시오.
  3. 녹색과 파란색 사이에 아주 작은 범위를 추가하여 네 번째 색깔을 넣어 보십시오.
  4. 픽셀 셰이더가 색상 대신 깊이값을 보여주게 해 보십시오.
  5. GraphicsClass.h의 near 평면과 far 평면의 값을 바꾸어 정확도가 어떻게 바뀌는지 확인해 보십시오.

소스 코드

비주얼 스튜디오 2010 프로젝트: dx11tut35.zip

소스코드만: dx11src35.zip

실행파일만: dx11exe35.zip

'강좌번역 > DirectX 11' 카테고리의 다른 글

DirectX11 Tutorial 37: 인스턴싱  (0) 2018.08.15
DirectX11 Tutorial 36: 블러  (0) 2018.07.03
DirectX11 Tutorial 34: 빌보드  (3) 2017.11.30
DirectX11 Tutorial 33: 불꽃  (0) 2017.10.25
DirectX11 Tutorial 32: 유리, 얼음  (0) 2017.09.23
Nav