DirectX11 Tutorial 38: 하드웨어 테셀레이션

강좌번역/DirectX 11 2018. 8. 18. 17:38 by 빠재

원문: Tutorial 38: Hardware Tessellation

하드웨어 테셀레이션은 이전 버전에는 없다가 이번 DirectX 11에 추가된 기능입니다. 이번 예제에서는 HLSL과 C++를 사용하여 하드웨어 테셀레이션을 하는 방법을 설명합니다.

지형과 모델에 테셀레이션을 사용하기 이전에는 소프트웨어에서 다각형 메시를 분할해야 했었습니다. 그렇게 분할이 되고 나면 그래픽 카드에 많은 수의 다각형을 보내게 됩니다. 이런 방법은 비효율적이고 렌더링 성능에도 병목을 만들었습니다. 하지만 DirectX 11과 셰이더 모델 5버전에서는 기본 다각형을 그래픽 카드에 보내고 그래픽 카드 안에서 특정 분할 알고리즘을 수행하게 하는 명령을 내리면 됩니다. 이 새로운 프로그래밍 가능한 헐 및 도메인 셰이더를 이용하여 분할 프로그램의 완전한 제어를 할 수 있습니다.

지금까지의 예제들에서는 대개 정점 셰이더와 픽셀 셰이더만을 작성했었습니다. 물론 그래픽스 파이프라인에 많은 단계가 있지만 일단 주요 관심은 일단 이 두 가지의 프로그래밍 가능한 부분에 있었습니다. 그래서 대보통 프로그램의 흐름은 아래 그림과 같았습니다:

하드웨어 테셀레이션을 사용하기 위하여 헐 및 도메인 셰이더를 추가하여 이전처럼 두 개가 아닌 네 개의 셰이더 프로그램을 작성하게 됩니다. 간단하게 설명했을 때 가장 큰 변화는 정점 셰이더가 세 부분으로 쪼개진 것입니다. 각 부분의 이름은 정점 셰이더, 헐 셰이더, 도메인 셰이더입니다. 테셀레이션은 헐과 도메인 셰이더에서 이루어집니다. 픽셀 셰이더는 그대로 있습니다. 이제 갱신된 프로그램 프름은 아래 그림과 같이 바뀝니다:

또한 이제 그래픽 카드에 삼각형 목록을 보내는 대신 패치 목록을 보내게 됩니다. 여기서 패치는 제어점들로 만들어진 기본 도형을 의미합니다. 예를 들어 그래픽 카드에 삼각형 하나를 보내면 그래픽 카드는 이것을 세 개의 제어점으로 된 하나의 패치로 인식합니다.

이 과정은 매우 복잡할 수 있지만 이번 예제에서는 테셀레이션의 기본만 보여드릴 생각입니다. 4번 예제를 수정하는데 녹색 삼각형 하나를 테셀레이션하여 많은 정점으로 이루어진 삼각형 메시로 만들 것입니다. 우선 어떻게 정점 셰이더를 수정하여 하드웨어 테셀레이션을 구현하는지 보겠습니다.

Color.vs

////////////////////////////////////////////////////////////////////////////////
// Filename: color.vs
////////////////////////////////////////////////////////////////////////////////


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

정점 셰이더의 첫번째 변화는 출력 구조체입니다. 이전의 출력은 픽셀 셰이더에 보내졌지만 이제는 그 대신 헐 셰이더에 보내집니다.

struct HullInputType
{
    float3 position : POSITION;
    float4 color : COLOR;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
HullInputType ColorVertexShader(VertexInputType input)
{
    HullInputType output;

정점 셰이더에서는 정점ㅈ과 색상 정보를 헐 셰이더에 전달합니다. 정점의 수가 테셀레이션을 통해 증가할 것이기 때문에 데이터를 가공하여 도메인 셰이더에서도 그렇게 합니다. 이제 정점 셰이더의 목적은 정점 애니메이션과 헐 셰이더에 정보를 전달하는 것입니다.

    // 정점 위치를 헐 혜이더에 전달합니다.
    output.position = input.position;
    
    // 입력 색상을 헐 셰이더에 전달합니다.
    output.color = input.color;
    
    return output;
}

Color.hs

HLSL 헐 셰이더는 패치 상수 함수와 헐 셰이더 자체 두 부분으로 구성되어 있습니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: color.hs
////////////////////////////////////////////////////////////////////////////////


/////////////
// GLOBALS //
/////////////

여기에 정의한 테셀레이션 상수 버퍼는 셰이더에 테셀레이션 계수를 제공하여 테셀레이터에 전달합니다. 이 버퍼는 렌더링되기 전에 ColorShaderClass에서 설정합니다.

cbuffer TessellationBuffer
{
    float tessellationAmount;
    float3 padding;
};


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

HullInputType은 정점 셰이더의 출력 구조와 동일합니다.

struct HullInputType
{
    float3 position : POSITION;
    float4 color : COLOR;
};

ConstantOutputType는 패치 상수 함수의 출력으로 사용될 구조체입니다.

struct ConstantOutputType
{
    float edges[3] : SV_TessFactor;
    float inside : SV_InsideTessFactor;
};

HullOutputType은 헐 셰이더의 출력으로 사용될 구조체입니다.

struct HullOutputType
{
    float3 position : POSITION;
    float4 color : COLOR;
};

ColorPatchConstantFunction은 헐 셰이더가 호출하는 패치 상수 함수입니다. 패치와 테셀레이션될 패치의 id를 입력으로 받습니다. 이 함수는 전체 패치에서 값이 변하지 않는 데이터를 설정/계산하는 데 사용됩니다. 이 예제에서는 삼각형의 세 꼭지점과 중심점을 위한 테셀레이션 계수를 설정합니다. 네 개의 계수를 모두 ColorShaderClass객체에서 온 tesselationAmount로 설정합니다. 이 함수는 헐 셰이더가 호출하고 이 출력은 테셀레이터에 전달됩니다.

////////////////////////////////////////////////////////////////////////////////
// Patch Constant Function
////////////////////////////////////////////////////////////////////////////////
ConstantOutputType ColorPatchConstantFunction(InputPatch<HullInputType, 3> inputPatch, uint patchId : SV_PrimitiveID)
{    
    ConstantOutputType output;


    // 삼각형 세 꼭지점의 테셀레이션 계수를 설정합니다.
    output.edges[0] = tessellationAmount;
    output.edges[1] = tessellationAmount;
    output.edges[2] = tessellationAmount;

    // 삼각형 안쪽의 테셀레이션 계수를 설정합니다.
    output.inside = tessellationAmount;

    return output;
}

헐 셰이더는 출력되는 각 제어점마다 불립니다. 또한 패치 상수 함수를 호출합니다. 헐 셰이더는 패치와 그 id를 입력으로 받습니다. 이 예제에서는 간단히 색상과 제어점들을 도메인 셰이더에 전달하지만 원한다면 여기에서 제어점들에 더 많은 계산과 수정들을 가할 수 있습니다.

또한 속성들을 정의하는 헤더를 보셨을 겁니다. 첫 번째 속성은 패치의 종류가 어떤 것인지 설명하는 도메인인데, 이번에는 삼각형으로 설정합니다. 두 번째 속성은 테셀레이션 분할 종류인데, 이 예제에서는 정수(integer)를 사용합니다. 세 번째 속성은 테셀레이터가 출력할 기본 도형의 종류인데, 여기에서는 삼각형을 선택합니다. 네 번째 속성은 출력할 제어점들의 수입니다. 이 예제에서는 각 패치마다 세 제어점들을 전달합니다. 그리고 마지막 속성은 헐 셰이더가 패치 상수 데이터를 입력으로 테셀레이터에 전달할 때 패시 상수 데이터를 설정하기 위해 호출할 상수 함수의 이름입니다.

////////////////////////////////////////////////////////////////////////////////
// Hull Shader
////////////////////////////////////////////////////////////////////////////////
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("ColorPatchConstantFunction")]

HullOutputType ColorHullShader(InputPatch<HullInputType, 3> patch, uint pointId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID)
{
    HullOutputType output;


    // 현재 제어점의 위치를 출력 위치로 사용합니다
    output.position = patch[pointId].position;

    // 입력 색상을 출력 색상으로 사용합니다.
    output.color = patch[pointId].color;

    return output;
}

Color.ds

도메인셰이더는 테셀레이션된 데이터를 받아 조작한 뒤 평소 정점 셰이더에서 하듯이 최종 정점으로 변환하는 일을 합니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: color.ds
////////////////////////////////////////////////////////////////////////////////


/////////////
// GLOBALS //
/////////////

이전과 같이 행렬 버퍼가 있지만 이번에는 정점 셰이더 대신 도메인 셰이더에 들어 있습니다. 변환되는 것은 테셀레이션이 끝난 데이터이지 테셀레이션되지 않은 정점 입력이 아니기 때문입니다.

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


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

도메인 셰이더는 헐 셰이더에서 넘어온 구조체를 입력으로 사용합니다.

struct ConstantOutputType
{
    float edges[3] : SV_TessFactor;
    float inside : SV_InsideTessFactor;
};

struct HullOutputType
{
    float3 position : POSITION;
    float4 color : COLOR;
};

도메인 셰이더의 출력은 픽셀 셰이더로 갑니다. 이전 예제에서는 이 부분이 정점 셰이더에 있었습니다.

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

도메인 셰이더는 도메인 속성 하나를 가지고 있습니다. 이 예제에서는 삼각형 패치로 설정합니다. 도메인 셰이더의 입력은 헐 셰이더와 상수 함수에서 옵니다. uvwCoords는 테셀레이터에서 오는데, 새로 생긴 정점에 각 제어점들의 가중치들입니다. 여기서 U는 첫번째 제어점, V는 두 번째 제어점, W가 세 번째입니다. 이 예제에서는 새 정점에 대한 세 제어점 모두 균등한 가중치를 가지고 있기 때문에 정점이 세 제어점의 중심에 위치하게 됩니다. 셰이더 첫 번째 줄에 있는 계산식이 보일 겁니다. 그리고 정점 위치가 정해졌기 때문에 이전과 같이 위치를 변환하고 픽셀 셰이더에 색상과 같이 전달합니다.

////////////////////////////////////////////////////////////////////////////////
// Domain Shader
////////////////////////////////////////////////////////////////////////////////
[domain("tri")]

PixelInputType ColorDomainShader(ConstantOutputType input, float3 uvwCoord : SV_DomainLocation, const OutputPatch<HullOutputType, 3> patch)
{
    float3 vertexPosition;
    PixelInputType output;
 

    // 새 정점의 위치를 정합니다.
    vertexPosition = uvwCoord.x * patch[0].position + uvwCoord.y * patch[1].position + uvwCoord.z * patch[2].position;
    
    // 새 정점의 위치를 월드, 뷰, 투영 행렬에 대해 계산합니다.
    output.position = mul(float4(vertexPosition, 1.0f), worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);

    // 입력 색상을 픽셀 셰이더에 전달합니다.
    output.color = patch[0].color;

    return output;
}

Color.ps

픽셀 셰이더는 원래 삼각형 튜토리얼과 똑같이 갑니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: color.ps
////////////////////////////////////////////////////////////////////////////////


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


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

Colorshaderclass.h

ColorShaderClass는 테셀레이션을 하기 위해 수정이 됩니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: colorshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _COLORSHADERCLASS_H_
#define _COLORSHADERCLASS_H_


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


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

헐 셰이더에 테셀레이션 강도를 설정하기 위한 테셀레이션 버퍼 구조체를 선언합니다.

    struct TessellationBufferType
    {
        float tessellationAmount;
        D3DXVECTOR3 padding;
    };

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

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

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

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

private:
    ID3D11VertexShader* m_vertexShader;

ColorShaderClass에서 사용하는 헐 및 도메인 셰이더입니다.

    ID3D11HullShader* m_hullShader;
    ID3D11DomainShader* m_domainShader;
    ID3D11PixelShader* m_pixelShader;
    ID3D11InputLayout* m_layout;
    ID3D11Buffer* m_matrixBuffer;

헐 셰이더에 테셀레이션 강도를 설정하기 위한 버퍼도 있습니다.

    ID3D11Buffer* m_tessellationBuffer;
};

#endif

Colorshaderclass.cpp

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

도메인 및 헐 셰이더와 테셀레이션 버퍼 포인터를 생성자에서 null로 초기화합니다.

ColorShaderClass::ColorShaderClass()
{
    m_vertexShader = 0;
    m_hullShader = 0;
    m_domainShader = 0;
    m_pixelShader = 0;
    m_layout = 0;
    m_matrixBuffer = 0;
    m_tessellationBuffer = 0;
}


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


ColorShaderClass::~ColorShaderClass()
{
}


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

InitializeShader 함수는 네 개의 셰이더 파일 이름을 받습니다. 모든 셰이더를 하나의 텍스트 파일로 만들 수도 있지만 명확한 코드를 위해 분리했습니다.

    // 셰이더를 초기화합니다.
    result = InitializeShader(device, hwnd, L"../Engine/color.vs", L"../Engine/color.hs", L"../Engine/color.ds", L"../Engine/color.ps");
    if(!result)
    {
        return false;
    }

    return true;
}


void ColorShaderClass::Shutdown()
{
    // 셰이더 및 관련된 객체들을 해제합니다.
    ShutdownShader();

    return;
}

Render 함수의 입력에 삼각형이 얼마나 테셀레이션되어야 할 지 정하는 tessellationAmount 변수를 추가로 받습니다. tessellationAmount 변수는 RenderShader 함수로 전달됩니다.

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


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

    // 셰이더로 버퍼를 그려냅니다.
    RenderShader(deviceContext, indexCount);

    return true;
}

InitializeShader 함수는 헐 및 도메인 셰이더도 사용하게 확장되었습니다.

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

셰이더 코드가 컴파일 될 수 있도록 잡아줄 ID3D10Blub 타입의 포인터를 선언합니다.

    ID3D10Blob* hullShaderBuffer;
    ID3D10Blob* domainShaderBuffer;
    ID3D10Blob* pixelShaderBuffer;
    D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
    unsigned int numElements;
    D3D11_BUFFER_DESC matrixBufferDesc;

또한 헐 셰이더의 테셀레이션 상수 버퍼를 위한 디스크립션 변수도 있습니다.

    D3D11_BUFFER_DESC tessellationBufferDesc;


    // 함수에서 사용할 포인터들을 null로 초기화합니다.
    errorMessage = 0;
    vertexShaderBuffer = 0;
    hullShaderBuffer = 0;
    domainShaderBuffer = 0;
    pixelShaderBuffer = 0;

    // 정점 셰이더 코드를 컴파일합니다.
    result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "ColorVertexShader", "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;
    }

이제 헐 셰이더 HLSL 코드를 컴파일합니다.

    // 헐 셰이더 코드를 컴파일합니다.
    result = D3DX11CompileFromFile(hsFilename, NULL, NULL, "ColorHullShader", "hs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
                       &hullShaderBuffer, &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, hsFilename);
        }
        // If there was nothing in the error message then it simply could not find the shader file itself.
        else
        {
            MessageBox(hwnd, hsFilename, L"Missing Shader File", MB_OK);
        }

        return false;
    }

그리고 나서 도메인 셰이더 HLSL 코드를 컴파일합니다.

    // 도메인 셰이더 코드를 컴파일합니다.
    result = D3DX11CompileFromFile(dsFilename, NULL, NULL, "ColorDomainShader", "ds_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
                       &domainShaderBuffer, &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, dsFilename);
        }
        // If there was nothing in the error message then it simply could not find the shader file itself.
        else
        {
            MessageBox(hwnd, dsFilename, L"Missing Shader File", MB_OK);
        }

        return false;
    }

    // 픽셀 셰이더 코드를 컴파일합니다.
    result = D3DX11CompileFromFile(psFilename, NULL, NULL, "ColorPixelShader", "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;
    }

컴파일된 HLSL 버퍼에서 헐 및 도메인 셰이더를 생성합니다.

    // 버퍼에서 헐 셰이더를 생성합니다.
    result = device->CreateHullShader(hullShaderBuffer->GetBufferPointer(), hullShaderBuffer->GetBufferSize(), NULL, &m_hullShader);
    if(FAILED(result))
    {
        return false;
    }

    // 버퍼에서 도메인 셰이더를 생성합니다.
    result = device->CreateDomainShader(domainShaderBuffer->GetBufferPointer(), domainShaderBuffer->GetBufferSize(), NULL, &m_domainShader);
    if(FAILED(result))
    {
        return false;
    }

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

    // 정점 입력 레이아웃 디스크립션입니다.
    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;

    // 레이아웃의 원소 개수를 구합니다.
    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;

    hullShaderBuffer->Release();
    hullShaderBuffer = 0;

    domainShaderBuffer->Release();
    domainShaderBuffer = 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;
    }

테셀레이션 상수 버퍼를 설정합니다.

    // 헐 셰이더에 있는 동적 테셀레이션 상수 버퍼의 디스크립션을 설정합니다.
    tessellationBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    tessellationBufferDesc.ByteWidth = sizeof(TessellationBufferType);
    tessellationBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    tessellationBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    tessellationBufferDesc.MiscFlags = 0;
    tessellationBufferDesc.StructureByteStride = 0;

    // 상수 버퍼의 퐁니터를 생성하여 이 클래스에서 헐 셰이더 상수 버퍼에 접근할 수 있게 합니다.
    result = device->CreateBuffer(&tessellationBufferDesc, NULL, &m_tessellationBuffer);
    if(FAILED(result))
    {
        return false;
    }

    return true;
}


void ColorShaderClass::ShutdownShader()
{

ShutdownShader 함수에서 테셀레이션 버퍼를 해제합니다.

    // 테셀레이션 상수 버퍼를 해제합니다.
    if(m_tessellationBuffer)
    {
        m_tessellationBuffer->Release();
        m_tessellationBuffer = 0;
    }

    // 행렬 상수 버퍼를 해제합니다.
    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;
    }

또한 ShutdownShader 함수에서 헐 및 도메인 셰이더를 해제합니다.

    // 도메인 셰이더를 해제합니다.
    if(m_domainShader)
    {
        m_domainShader->Release();
        m_domainShader = 0;
    }

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

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

    return;
}


void ColorShaderClass::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;
}

행렬들이 도메인 셰이더에 있고 테셀레이션 강도가 헐 셰이더에서 설정되기 때문에 SetShaderParameters 함수를 수정합니다.

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


    // 셰이더를 준비하기 위해 행렬들을 전치합니다.
    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->DSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);

헐 셰이더의 테셀레이션 상수 버퍼를 설정합니다.

    // 버퍼에 쓰기 위하여 테셀레이션 상수 버퍼를 잠급니다.
    result = deviceContext->Map(m_tessellationBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // 테셀레이션 상수 버퍼의 데이터 포인터를 얻어옵니다.
    dataPtr2 = (TessellationBufferType*)mappedResource.pData;

    // 테셀레이션 데이터를 상수 버퍼에 복사합니다.
    dataPtr2->tessellationAmount = tessellationAmount;
    dataPtr2->padding = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

    // 테셀레이션 상수 버퍼의 잠금을 해제합니다.
    deviceContext->Unmap(m_tessellationBuffer, 0);

    // 헐 셰이더에 있는 테셀레이션 상수 버퍼의 위치입니다.
    bufferNumber = 0;

    // 헐 셰이더의 테셀레이션 상수 버퍼를 생신된 값을 설정합니다.
    deviceContext->HSSetConstantBuffers(bufferNumber, 1, &m_tessellationBuffer);

    return true;
}


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

또한 RenderShader 함수에서 헐 및 도메인 셰이더를 설정합니다.

    // 삼각형을 그릴 때 쓰일 정점, 헐, 도메인, 픽셀 셰이더를 설정합니다.
    deviceContext->VSSetShader(m_vertexShader, NULL, 0);
    deviceContext->HSSetShader(m_hullShader, NULL, 0);
    deviceContext->DSSetShader(m_domainShader, NULL, 0);
    deviceContext->PSSetShader(m_pixelShader, NULL, 0);

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

    return;
}

Modelclass.h

ModelClass의 헤더 파일은 이전과 달라지지 않습니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_


//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>


////////////////////////////////////////////////////////////////////////////////
// Class name: ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:
    struct VertexType
    {
        D3DXVECTOR3 position;
        D3DXVECTOR4 color;
    };

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

    bool Initialize(ID3D11Device*);
    void Shutdown();
    void Render(ID3D11DeviceContext*);

    int GetIndexCount();

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

private:
    ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
    int m_vertexCount, m_indexCount;
};

#endif

Modelclass.cpp

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


ModelClass::ModelClass()
{
    m_vertexBuffer = 0;
    m_indexBuffer = 0;
}


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


ModelClass::~ModelClass()
{
}


bool ModelClass::Initialize(ID3D11Device* device)
{
    bool result;


    // 정점 및 인덱스 버퍼를 초기화합니다.
    result = InitializeBuffers(device);
    if(!result)
    {
        return false;
    }

    return true;
}


void ModelClass::Shutdown()
{
    // 정점 및 인덱스 버퍼를 해제합니다.
    ShutdownBuffers();

    return;
}


void ModelClass::Render(ID3D11DeviceContext* deviceContext)
{
    // 정점 및 인덱스 버퍼를 그래픽스 파이프라인에 넣어 그려질 준비를 합니다.
    RenderBuffers(deviceContext);

    return;
}


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


bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
    VertexType* vertices;
    unsigned long* indices;
    D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
    D3D11_SUBRESOURCE_DATA vertexData, indexData;
    HRESULT result;


    // 정점 배열의 정점 수입니다.
    m_vertexCount = 3;

    // 인덱스 배열의 인덱스 수입니다.
    m_indexCount = 3;

    // 정점 배열을 생성합니다.
    vertices = new VertexType[m_vertexCount];
    if(!vertices)
    {
        return false;
    }

    // 인덱스 배열을 생성합니다.
    indices = new unsigned long[m_indexCount];
    if(!indices)
    {
        return false;
    }

    // 정점 배열에 데이터를 로드합니다.
    vertices[0].position = D3DXVECTOR3(-1.0f, -1.0f, 0.0f);  // Bottom left.
    vertices[0].color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);

    vertices[1].position = D3DXVECTOR3(0.0f, 1.0f, 0.0f);  // Top middle.
    vertices[1].color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);

    vertices[2].position = D3DXVECTOR3(1.0f, -1.0f, 0.0f);  // Bottom right.
    vertices[2].color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);

    // 인덱스 배열에 데이터를 로드합니다.
    indices[0] = 0;  // Bottom left.
    indices[1] = 1;  // Top middle.
    indices[2] = 2;  // Bottom right.

    // 정적 정점 버퍼의 디스크립션을 설정합니다.
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

    // 보조 리소스에 정점 데이터의 포인터를 설정합니다.
    vertexData.pSysMem = vertices;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;

    // 정점 버퍼를 생성합니다.
    result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
    if(FAILED(result))
    {
        return false;
    }

    // 정적 인덱스 버퍼의 디스크립션을 설정합니다.
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;

    // 보조 리소스에 인덱스 데이터의 포인터를 설정합니다.
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;

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

    // 정점 및 인덱스 버퍼가 생성되었기 때문에 배열을 해제합니다.
    delete [] vertices;
    vertices = 0;

    delete [] indices;
    indices = 0;

    return true;
}


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

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

    return;
}


void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
    unsigned int stride;
    unsigned int offset;


    // 정점 버퍼의 스트라이드와 오프셋입니다.
    stride = sizeof(VertexType); 
    offset = 0;
    
    // 입력 어셈블러에 정점 버퍼를 활성화하여 그려질 수 있도록 합니다.
    deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

    // 입력 어셈블러에 인덱스 버퍼를 활성화하여 그려질 수 있도록 합니다.
    deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

ModelClass의 유일한 변경점은 삼각형 목록 대신 제어점 패치 목록을 그린다는 것입니다. 테셀레이션이 동작하려면 필요한 수정사항입니다.

    // 이 정점 버퍼에서 그릴 도형의 종류를 설정합니다.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);

    return;
}

D3dclass.cpp

이 예제에서는 삼각형의 와이어프레임을 그려내고 싶기 때문에 D3DClass::Initialize 함수에서 수정을 하나 합니다.

bool D3DClass::Initialize(int screenWidth, int screenHeight, bool vsync, HWND hwnd, bool fullscreen, float screenDepth, float screenNear)
{
    HRESULT result;
    IDXGIFactory* factory;
    IDXGIAdapter* adapter;
    IDXGIOutput* adapterOutput;
    unsigned int numModes, i, numerator, denominator, stringLength;
    DXGI_MODE_DESC* displayModeList;
    DXGI_ADAPTER_DESC adapterDesc;
    int error;
    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    D3D_FEATURE_LEVEL featureLevel;
    ID3D11Texture2D* backBufferPtr;
    D3D11_TEXTURE2D_DESC depthBufferDesc;
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
    D3D11_RASTERIZER_DESC rasterDesc;
    D3D11_VIEWPORT viewport;
    float fieldOfView, screenAspect;


    // 수직동기화 설정을 저장합니다.
    m_vsync_enabled = vsync;

    // DirectX 그래픽스 인터페이스 팩토리를 생성합니다.
    result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
    if(FAILED(result))
    {
        return false;
    }

    // 팩토리 변수를 사용하여 1차 그래픽스 인터페이스의 어댑터를 생성합니다(그래픽 카드).
    result = factory->EnumAdapters(0, &adapter);
    if(FAILED(result))
    {
        return false;
    }

    // 1차 어댑터 출력을 나열합니다(모니터). Enumerate the primary adapter output (monitor).
    result = adapter->EnumOutputs(0, &adapterOutput);
    if(FAILED(result))
    {
        return false;
    }

    // 모니터 출력을 위해 DXGI_FORMAT_R8G8B8A8_UNORM 디스플레이 포맷에 맞는 모드의 수를 구합니다.
    result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
    if(FAILED(result))
    {
        return false;
    }

    // 이 모니터/그래픽 카드의 가능한 모든 조합을 저장할 배열을 생성합니다.
    displayModeList = new DXGI_MODE_DESC[numModes];
    if(!displayModeList)
    {
        return false;
    }

    // 디스플레이 모드 배열을 채웁니다.
    result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
    if(FAILED(result))
    {
        return false;
    }

    // 모든 디스플레이 모드들을 보면서 화면의 너비와 높이에 맞는 것을 찾습니다.
    // 조건에 맞는 것이 있다면 모니터 주사율의 numerator와 denomninator을 저장합니다.
    for(i=0; i<numModes; i++)
    {
        if(displayModeList[i].Width == (unsigned int)screenWidth)
        {
            if(displayModeList[i].Height == (unsigned int)screenHeight)
            {
                numerator = displayModeList[i].RefreshRate.Numerator;
                denominator = displayModeList[i].RefreshRate.Denominator;
            }
        }
    }

    // 어댑터(그래픽 카드)의 디스크립션을 가져옵니다.
    result = adapter->GetDesc(&adapterDesc);
    if(FAILED(result))
    {
        return false;
    }

    // 전용 그래픽 카드 메모리의 크기를 메가바이트 단위로 저장합니다.
    m_videoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);

    // 그래픽 카드의 이름을 캐릭터 배열로 저장합니다.
    error = wcstombs_s(&stringLength, m_videoCardDescription, 128, adapterDesc.Description, 128);
    if(error != 0)
    {
        return false;
    }

    // 디스플레이 모드 배열을 해제합니다.
    delete [] displayModeList;
    displayModeList = 0;

    // 어댑터 출력을 해제합니다.
    adapterOutput->Release();
    adapterOutput = 0;

    // 어댑터를 해제합니다.
    adapter->Release();
    adapter = 0;

    // 팩토리를 해제합니다.
    factory->Release();
    factory = 0;

    // 스왑 체인 디스크립션을 초기화합니다.
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

    // 백버퍼 수를 하나로 설정합니다.
    swapChainDesc.BufferCount = 1;

    // 백버퍼의 너비 및 높이를 설정합니다.
    swapChainDesc.BufferDesc.Width = screenWidth;
    swapChainDesc.BufferDesc.Height = screenHeight;

    // 백버퍼를 일반 32비트 표면으로 설정합니다.
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

    // 백버퍼의 주사율을 설정합니다.
    if(m_vsync_enabled)
    {
        swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
        swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
    }
    else
    {
        swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
        swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    }

    // 백버퍼의 용도 변수를 설정합니다.
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

    // 렌더링될 윈도우의 핸들을 설정합니다.
    swapChainDesc.OutputWindow = hwnd;

    // 멀티샘플링을 끕니다.
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;

    // 풀스크린 또는 윈도우 모드를 설정합니다.
    if(fullscreen)
    {
        swapChainDesc.Windowed = false;
    }
    else
    {
        swapChainDesc.Windowed = true;
    }

    // 스캔 라인 정렬 및 크기 조정을 설정되지 않음으로 설정합니다.
    swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

    // 화면에 표시하고 난 뒤 백버퍼 상수를 버립니다.
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    // 고급 플래그를 설정하지 않습니다.
    swapChainDesc.Flags = 0;

    // 기능 레벨을 DirectX 11로 설정합니다.
    featureLevel = D3D_FEATURE_LEVEL_11_0;

    // 스왑 체인, Direct3D 디바이스, Direct3D 디바이스 컨텍스트를 생성합니다.
    result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1, 
                           D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_device, NULL, &m_deviceContext);
    if(FAILED(result))
    {
        return false;
    }

    // 백버퍼의 포인터를 얻어옵니다.
    result = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
    if(FAILED(result))
    {
        return false;
    }

    // 백버퍼의 포인터로 렌더 타겟 뷰를 생성합니다.
    result = m_device->CreateRenderTargetView(backBufferPtr, NULL, &m_renderTargetView);
    if(FAILED(result))
    {
        return false;
    }

    // 더 이상 사용하지 않는 백버퍼 포인터를 해제합니다.
    backBufferPtr->Release();
    backBufferPtr = 0;

    // 깊이 버퍼의 디스크립션을 초기화합니다.
    ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));

    // 깊이 버퍼의 디스크립션을 설정합니다.
    depthBufferDesc.Width = screenWidth;
    depthBufferDesc.Height = screenHeight;
    depthBufferDesc.MipLevels = 1;
    depthBufferDesc.ArraySize = 1;
    depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthBufferDesc.SampleDesc.Count = 1;
    depthBufferDesc.SampleDesc.Quality = 0;
    depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    depthBufferDesc.CPUAccessFlags = 0;
    depthBufferDesc.MiscFlags = 0;

    // 채워진 디스크립션을 사용하여 뎁스 버퍼의 텍스쳐를 생성합니다.
    result = m_device->CreateTexture2D(&depthBufferDesc, NULL, &m_depthStencilBuffer);
    if(FAILED(result))
    {
        return false;
    }

    // 스텐실 스테이트의 디스크립션을 초기화합니다.
    ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));

    // 스텐실 스테이트의 디스크립션을 설정합니다.
    depthStencilDesc.DepthEnable = true;
    depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;

    depthStencilDesc.StencilEnable = true;
    depthStencilDesc.StencilReadMask = 0xFF;
    depthStencilDesc.StencilWriteMask = 0xFF;

    // 픽셀이 정면을 보고 있을 때의 스텐실 동작입니다.
    depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
    depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

    // 픽셀이 뒤를 보고 있을 때의 스텐실 동작입니다.
    depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
    depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

    // 스텐실 스테이트를 생성합니다.
    result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
    if(FAILED(result))
    {
        return false;
    }

    // 깊이 스텐실 스테이트를 설정합니다.
    m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);

    // 깊이 스텐실 뷰 디스크립션을 초기화합니다.
    ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));

    // 깊이 스텐실 뷰 디스크립션을 설정합니다.
    depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
    depthStencilViewDesc.Texture2D.MipSlice = 0;

    // 깊이 스텐실 뷰를 생성합니다.
    result = m_device->CreateDepthStencilView(m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView);
    if(FAILED(result))
    {
        return false;
    }

    // 렌더 타겟 뷰와 깊이 스텐실 버퍼를 출력 렌더링 파이프라인에 바인딩합니다.
    m_deviceContext->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);

이 예제에서는 래스터 디스크립션을 와이어프레임으로 설정합니다.

    // 어떤 도형이 그려질 지 래스터 디스크립션을 설정합니다.
    rasterDesc.AntialiasedLineEnable = false;
    rasterDesc.CullMode = D3D11_CULL_BACK;
    rasterDesc.DepthBias = 0;
    rasterDesc.DepthBiasClamp = 0.0f;
    rasterDesc.DepthClipEnable = true;
    rasterDesc.FillMode = D3D11_FILL_WIREFRAME;
    rasterDesc.FrontCounterClockwise = false;
    rasterDesc.MultisampleEnable = false;
    rasterDesc.ScissorEnable = false;
    rasterDesc.SlopeScaledDepthBias = 0.0f;

    // 채워진 디스크립션에서 래스터라이저 스테이트를 생성합니다.
    result = m_device->CreateRasterizerState(&rasterDesc, &m_rasterState);
    if(FAILED(result))
    {
        return false;
    }

    // 래스터라이저 스테이트를 설정합니다.
    m_deviceContext->RSSetState(m_rasterState);
    
    // 렌더링할 뷰포트를 설정합니다.
    viewport.Width = (float)screenWidth;
    viewport.Height = (float)screenHeight;
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;

    // 뷰포트를 생성합니다.
    m_deviceContext->RSSetViewports(1, &viewport);

    // 투영 행렬을 설정합니다.
    fieldOfView = (float)D3DX_PI / 4.0f;
    screenAspect = (float)screenWidth / (float)screenHeight;

    // 3D 렌더링에 사용할 투영 행렬을 생성합니다.
    D3DXMatrixPerspectiveFovLH(&m_projectionMatrix, fieldOfView, screenAspect, screenNear, screenDepth);

    // 월드 행렬을 단위 행렬로 초기화합니다.
    D3DXMatrixIdentity(&m_worldMatrix);

    // 2D 렌더링을 위한 직교 투영 행렬을 생성합니다.
    D3DXMatrixOrthoLH(&m_orthoMatrix, (float)screenWidth, (float)screenHeight, screenNear, screenDepth);

    return true;
}

Graphicsclass.h

GraphicsClass 헤어 및 소스 파일은 변경되지 않습니다. 단지 참고를 위해 넣어두었습니다.

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


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


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


////////////////////////////////////////////////////////////////////////////////
// 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;
    ColorShaderClass* m_ColorShader;
};

#endif

Graphicsclass.cpp

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


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


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


GraphicsClass::~GraphicsClass()
{
}


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


    // Create the Direct3D object.
    m_D3D = new D3DClass;
    if(!m_D3D)
    {
        return false;
    }

    // Initialize the Direct3D object.
    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;
    }

    // Create the camera object.
    m_Camera = new CameraClass;
    if(!m_Camera)
    {
        return false;
    }

    // Set the initial position of the camera.
    m_Camera->SetPosition(0.0f, 0.0f, -10.0f);
    
    // Create the model object.
    m_Model = new ModelClass;
    if(!m_Model)
    {
        return false;
    }

    // Initialize the model object.
    result = m_Model->Initialize(m_D3D->GetDevice());
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
        return false;
    }

    // Create the color shader object.
    m_ColorShader = new ColorShaderClass;
    if(!m_ColorShader)
    {
        return false;
    }

    // Initialize the color shader object.
    result = m_ColorShader->Initialize(m_D3D->GetDevice(), hwnd);
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize the color shader object.", L"Error", MB_OK);
        return false;
    }

    return true;
}


void GraphicsClass::Shutdown()
{
    // Release the color shader object.
    if(m_ColorShader)
    {
        m_ColorShader->Shutdown();
        delete m_ColorShader;
        m_ColorShader = 0;
    }

    // Release the model object.
    if(m_Model)
    {
        m_Model->Shutdown();
        delete m_Model;
        m_Model = 0;
    }

    // Release the camera object.
    if(m_Camera)
    {
        delete m_Camera;
        m_Camera = 0;
    }

    // Release the D3D object.
    if(m_D3D)
    {
        m_D3D->Shutdown();
        delete m_D3D;
        m_D3D = 0;
    }

    return;
}


bool GraphicsClass::Frame()
{
    bool result;


    // Render the graphics scene.
    result = Render();
    if(!result)
    {
        return false;
    }

    return true;
}


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


    // Clear the buffers to begin the scene.
    m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

    // Generate the view matrix based on the camera's position.
    m_Camera->Render();

    // Get the world, view, and projection matrices from the camera and d3d objects.
    m_Camera->GetViewMatrix(viewMatrix);
    m_D3D->GetWorldMatrix(worldMatrix);
    m_D3D->GetProjectionMatrix(projectionMatrix);

    // Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
    m_Model->Render(m_D3D->GetDeviceContext());

    // Render the model using the color shader.
    result = m_ColorShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, 12.0f);
    if(!result)
    {
        return false;
    }

    // Present the rendered scene to the screen.
    m_D3D->EndScene();

    return true;
}

마치면서

이제 CPU대신 GPU를 사용하여 메시의 다각형들을 분할할 수 있습니다. 더 고급의 예제를 원하신다면 DirectX SDK에 있는 SubD11 예제를 참고할 수 있습니다.

연습 문제

  1. 프로그램을 다시 컴파일하여 실행해 보십시오. 테셀레이션된 와이어프레임 녹색 삼각형이 보일 것입니다. ESC키로 종료합니다.
  2. 테셀레이션 강도를 바꾸어 삼각형의 테셀레이션이 어떤 영향을 미치는지 확인해 보십시오.
  3. 도메인 셰이더에서 새 정점의 테셀레이션에 영향을 미치는 uvwCoord 가중치를 수정해 보십시오.
  4. DirectX SDK에 있는 SubD11 예제의 HLSL 파일을 살펴보십시오.

소스 코드

Visual Studio 2010 프로젝트: dx11tut38.zip

소스코드만: dx11src38.zip

실행파일만: dx11exe38.zip

Nav