DirectX11 Tutorial 24: 클리핑 평면

강좌번역/DirectX 11 2015. 3. 21. 14:18 by 빠재

Tutorial 24: Clipping Planes

원문: http://rastertek.com/dx11tut24.html

이번 튜토리얼에서는 HLSL과 C++을 이용하여 클리핑 평면을 만드는 방법을 다룰 것입니다. 이 튜토리얼의 코드는 이전 코드에서 이어집니다.

클리핑 평면은 일반적으로 3D공간에서 기하학적 구조를 잘라내는 데 사용됩니다. 가장 대표적인 예로 뷰포트를 만들 때 사용하는 nearfar 평면이 있습니다. 뷰포트의 far평면 뒤로 이어지는 것들을 잘라내기 때문에 뒤에 나타나는 거대한 3D 구조를 그리지 않게 해 줍니다.

이전 버전의 DirectX를 사용했던 사람은 DirectX 10/11에서의 클리핑 평면이 다르게 구현되었다는 것을 알 것입니다. 이전에는 평면 오브젝트를 만들고 SetRenderState 함수를 사용하여 클리핑 평면이 활성화되도록 했습니다. 그 대신에 DirectX 11에서는 똑같은 용도로 픽셀 셰이더의 입력 구조체에 SV_ClipDistance이라는 시맨틱을 사용합니다.

이 튜토리얼에서는 클리핑 평면을 만들어 이를 넘어가는 것들은 잘라내도록 하는 법을 다룰 것입니다. 이후 다른 튜토리얼에서 다루겠지만 RTT와 클리핑 평면이 같이 사용되면 매우 효과적인 장면을 연출할 수 있습니다.


프레임워크

프레임워크에 ClipPlaneShaderClass가 추가되었습니다. 이 클래스는 TextureShaderClass를 약간 변형한 것입니다.


Clipplane.vs

이 HLSL 파일은 기본 텍스쳐 셰이더에 한 개의 클리핑 평면을 다룰 수 있도록 한 것입니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: clipplane.vs
////////////////////////////////////////////////////////////////////////////////


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

우리는 클리핑 평면의 부동소수점 벡터를 4개를 가지고 있으므로 외부 오브젝트는 클리핑 평면이 어디에 있는지, 평면의 어느 쪽이 클리핑될 것인지 정할 수 있습니다. 4번째 숫자는 위치를 지정합니다. 예를 들어 벡터의 값이 (0.0f, 1.0f, 0.0f, -5.0f)라면 이 평면은 Y축 아래에 있는 모든 것들을 잘라낼 것이고 높이 5.0부터 시작할 것입니다. 따라서 어떤 픽셀이던지 y값이 5.0 미만이라면 그려지지 않을 것입니다. 이 튜토리얼에서는 (0.0f, -1.0f, 0.0f, 0.0f) 벡터를 사용할 것입니다. 다시 말해, y값이 0.0보다 크면 그려지지 않을 것입니다.

cbuffer ClipPlaneBuffer
{
    float4 clipPlane;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;

여기에 픽셀 셰이더의 입력으로 들어갈 클리핑 평면 시맨틱을 추가합니다.

    float clip : SV_ClipDistance0;
};

////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType ClipPlaneVertexShader(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.tex = input.tex;

픽셀 셰이더의 입력으로 들어갈 클리핑 평면을 설정하기 위해 월드 공간의 정점들과 클리핑 평면에 내적을 수행합니다. 그렇게 하여 클리핑 평면이 활성화되고 픽셀 셰이더는 클리핑 평면이 컬링한 것들을 그리지 않게 됩니다.

    // 클리핑 평면을 설정합니다.
    output.clip = dot(mul(input.position, worldMatrix), clipPlane);

    return output;
}

Clipplane.ps

정점 셰이더로부터의 입력과 맞추기 위해 PixelInputType 구조체를 수정합니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: clipplane.ps
////////////////////////////////////////////////////////////////////////////////


/////////////
// GLOBALS //
/////////////
Texture2D shaderTexture;
SamplerState SampleType;


//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float clip : SV_ClipDistance0;
};


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ClipPlanePixelShader(PixelInputType input) : SV_TARGET
{
    return shaderTexture.Sample(SampleType, input.tex);
}

Clipplaneshaderclass.h

ClipPlaneShaderClassTextureShaderClass가 클리핑을 처리할 수 있도록 한 것입니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: clipplaneshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _CLIPPLANESHADERCLASS_H_
#define _CLIPPLANESHADERCLASS_H_


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


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

정점 셰이더의 클리핑 평면 상수 버퍼에 해당하는 구조체를 선언합니다.

    struct ClipPlaneBufferType
    {
        D3DXVECTOR4 clipPlane;
    };

public:
    ClipPlaneShaderClass();
    ClipPlaneShaderClass(const ClipPlaneShaderClass&amp;);
    ~ClipPlaneShaderClass();

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

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

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

private:
    ID3D11VertexShader* m_vertexShader;
    ID3D11PixelShader* m_pixelShader;
    ID3D11InputLayout* m_layout;
    ID3D11Buffer* m_matrixBuffer;
    ID3D11SamplerState* m_sampleState;

클리핑 평면 정보를 저장할 상수 버퍼를 선언합니다.

    ID3D11Buffer* m_clipPlaneBuffer;
};

#endif

Clipplaneshaderclass.cpp

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


ClipPlaneShaderClass::ClipPlaneShaderClass()
{
    m_vertexShader = 0;
    m_pixelShader = 0;
    m_layout = 0;
    m_matrixBuffer = 0;
    m_sampleState = 0;

클리핑 평면 버퍼를 null로 초기화합니다.

    m_clipPlaneBuffer = 0;
}


ClipPlaneShaderClass::ClipPlaneShaderClass(const ClipPlaneShaderClass&amp; other)
{
}


ClipPlaneShaderClass::~ClipPlaneShaderClass()
{
}


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

클리핑 평면의 HLSL파일을 로드합니다.

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

    return true;
}


void ClipPlaneShaderClass::Shutdown()
{
    // 정점 셰이더와 픽셀 셰이더를 다른 객체들과 함께 내립니다.
    ShutdownShader();

    return;
}

Render함수는 클리핑 평면에 해당하는 4차 벡터를 입력으로 받습니다. 이 클리핑 평면은 렌더링되기 전에 셰이더에 세팅됩니다.

bool ClipPlaneShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, 
                  D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, 
                  D3DXVECTOR4 clipPlane)
{
    bool result;


    // 렌더링에 사용될 셰이더 인자들을 설정합니다.
    result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, clipPlane);
    if(!result)
    {
        return false;
    }

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

    return true;
}


bool ClipPlaneShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
    HRESULT result;
    ID3D10Blob* errorMessage;
    ID3D10Blob* vertexShaderBuffer;
    ID3D10Blob* pixelShaderBuffer;
    D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
    unsigned int numElements;
    D3D11_BUFFER_DESC matrixBufferDesc;
    D3D11_SAMPLER_DESC samplerDesc;
    D3D11_BUFFER_DESC clipPlaneBufferDesc;


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

클리핑 평면 정점 셰이더를 로드합니다.

    // 정점 셰이더 코드를 컴파일합니다.
    result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "ClipPlaneVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 
                       0, NULL, &amp;vertexShaderBuffer, &amp;errorMessage, NULL);
    if(FAILED(result))
    {
        // 컴파일이 실패했다면 무언가 에러 메세지가 남았을 것입니다.
        if(errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
        }
        // 만약 에러 메세지가 없다면 셰이더 자체를 찾지 못한 것입니다.
        else
        {
            MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
        }

        return false;
    }

클리핑 평면 픽셀 셰이더를 로드합니다.

    // 픽셀 셰이더 코드를 컴파일합니다.
    result = D3DX11CompileFromFile(psFilename, NULL, NULL, "ClipPlanePixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 
                       0, NULL, &amp;pixelShaderBuffer, &amp;errorMessage, NULL);
    if(FAILED(result))
    {
        // 컴파일이 실패했다면 무언가 에러 메세지가 남았을 것입니다.
        if(errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
        }
        // 만약 에러 메세지가 없다면 셰이더 자체를 찾지 못한 것입니다.
        else
        {
            MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
        }

        return false;
    }

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

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

    // 정점 입력 레이아웃 description을 작성합니다.
    // 이것은 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;

    polygonLayout[1].SemanticName = "TEXCOORD";
    polygonLayout[1].SemanticIndex = 0;
    polygonLayout[1].Format = DXGI_FORMAT_R32G32_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-&gt;CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer-&gt;GetBufferPointer(), 
                       vertexShaderBuffer-&gt;GetBufferSize(), &amp;m_layout);
    if(FAILED(result))
    {
        return false;
    }

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

    pixelShaderBuffer-&gt;Release();
    pixelShaderBuffer = 0;

    // 정점 셰이더에 있는 동적 상수 버퍼의 description을 세팅합니다.
    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, &amp;m_matrixBuffer);
    if(FAILED(result))
    {
        return false;
    }

    // 텍스쳐 샘플러의 description을 만듭니다.
    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.MipLODBias = 0.0f;
    samplerDesc.MaxAnisotropy = 1;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
    samplerDesc.BorderColor[0] = 0;
    samplerDesc.BorderColor[1] = 0;
    samplerDesc.BorderColor[2] = 0;
    samplerDesc.BorderColor[3] = 0;
    samplerDesc.MinLOD = 0;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

    // 텍스쳐 샘플러를 생성합니다.
    result = device-&gt;CreateSamplerState(&amp;samplerDesc, &amp;m_sampleState);
    if(FAILED(result))
    {
        return false;
    }

클리핑 평면 버퍼를 세팅합니다.

    // 정점 셰이더에 있는 클리핑 평면의 동적 상수 버퍼를 설정합니다.
    clipPlaneBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    clipPlaneBufferDesc.ByteWidth = sizeof(ClipPlaneBufferType);
    clipPlaneBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    clipPlaneBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    clipPlaneBufferDesc.MiscFlags = 0;
    clipPlaneBufferDesc.StructureByteStride = 0;

    // 상수 버퍼를 생성합니다.
    result = device-&gt;CreateBuffer(&amp;clipPlaneBufferDesc, NULL, &amp;m_clipPlaneBuffer);
    if(FAILED(result))
    {
        return false;
    }

    return true;
}


void ClipPlaneShaderClass::ShutdownShader()
{

ShutdownShader 함수에서는 클리핑 평면 버퍼를 해제합니다.

    // 클리핑 평면의 상수 버퍼를 해제합니다.
    if(m_clipPlaneBuffer)
    {
        m_clipPlaneBuffer-&gt;Release();
        m_clipPlaneBuffer = 0;
    }

    // 샘플러를 해제합니다.
    if(m_sampleState)
    {
        m_sampleState-&gt;Release();
        m_sampleState = 0;
    }

    // 행렬 상수 버퍼를 해제합니다.
    if(m_matrixBuffer)
    {
        m_matrixBuffer-&gt;Release();
        m_matrixBuffer = 0;
    }

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

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

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

    return;
}


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


    // 에러 메세지에 대한 포인터를 얻어옵니다.
    compileErrors = (char*)(errorMessage-&gt;GetBufferPointer());

    // 메세지의 길이를 구합니다.
    bufferSize = errorMessage-&gt;GetBufferSize();

    // 에러 메세지가 출력될 파일을 엽니다.
    fout.open("shader-error.txt");

    // 에러 메세지를 출력합니다.
    for(i=0; i&lt;buffersize; i++) {
        fout << compileerrors[i];
    }
    
    // 파일을 닫습니다.
    fout.close();

    // 에러 메세지를 해제합니다.
    errormessage-&gt;Release();
    errorMessage = 0;

    // 텍스트 파일을 열어 컴파일 에러를 확인하라는 팝업을 출력합니다.
    MessageBox(hwnd, L"Error compiling shader.  Check shader-error.txt for message.", shaderFilename, MB_OK);

    return;
}


bool ClipPlaneShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, 
                           D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, 
                           D3DXVECTOR4 clipPlane)
{
    HRESULT result;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    MatrixBufferType* dataPtr;
    unsigned int bufferNumber;
    ClipPlaneBufferType* dataPtr2;


    // 행렬을 변환하여 셰이더에 사용할 준비를 합니다.
    D3DXMatrixTranspose(&amp;worldMatrix, &amp;worldMatrix);
    D3DXMatrixTranspose(&amp;viewMatrix, &amp;viewMatrix);
    D3DXMatrixTranspose(&amp;projectionMatrix, &amp;projectionMatrix);

    // 행렬 상수 버퍼에 내용을 쓸 수 있도록 락을 겁니다.
    result = deviceContext-&gt;Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &amp;mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // 행렬 상수 버퍼의 데이터 포인터를 가져옵니다.
    dataPtr = (MatrixBufferType*)mappedResource.pData;

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

    // 버퍼의 락을 해제합니다.
    deviceContext-&gt;Unmap(m_matrixBuffer, 0);

    // 정점 셰이더 상의 행렬 상수 버퍼의 위치를 설정합니다.
    bufferNumber = 0;

    // 정점 셰이더에 행렬 상수 버퍼를 새것으로 업데이트합니다.
    deviceContext-&gt;VSSetConstantBuffers(bufferNumber, 1, &amp;m_matrixBuffer);

    // 픽셀 셰이더의 셰이더 텍스쳐 리소스를 설정합니다.
    deviceContext-&gt;PSSetShaderResources(0, 1, &amp;texture);

클리핑 평면의 상수 버퍼에 락을 걸고, 클리핑 평면의 값을 설정하고 락을 해제합니다. 이렇게 셰이더 안의 클리핑 평면의 데이터가 바뀌게 됩니다. 또한 이건 정점 셰이더의 두 번째 버퍼이기 때문에 bufferNumber을 0에서 1로 늘려줘야 합니다.

    // 값을 쓸 수 있도록 클리핑 평면의 상수 버퍼에 락을 겁니다.
    result = deviceContext-&gt;Map(m_clipPlaneBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &amp;mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // 클리핑 평면 상수 버퍼의 데이터 포인터를 얻어옵니다.
    dataPtr2 = (ClipPlaneBufferType*)mappedResource.pData;

    // 클리핑 평면을 상수 버퍼에 복사합니다.
    dataPtr2-&gt;clipPlane = clipPlane;

    // 버퍼의 락을 해제합니다.
    deviceContext-&gt;Unmap(m_clipPlaneBuffer, 0);

    // 정점 셰이더 상의 클리핑 평면 상수 버퍼의 위치를 설정합니다.
    bufferNumber = 1;

    // 클리핑 평면 상수 버퍼를 새 값으로 업데이트합니다.
    deviceContext-&gt;VSSetConstantBuffers(bufferNumber, 1, &amp;m_clipPlaneBuffer);

    return true;
}


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

    // 삼각형을 렌더링하기 위한 정점 및 픽셀 셰이더를 설정합니다.
    deviceContext-&gt;VSSetShader(m_vertexShader, NULL, 0);
    deviceContext-&gt;PSSetShader(m_pixelShader, NULL, 0);

    // 픽셀 셰이더의 샘플러를 설정합니다.
    deviceContext-&gt;PSSetSamplers(0, 1, &amp;m_sampleState);

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

    return;
}

Graphicsclass.h

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


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


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

클리핑 평면 셰이더 클래스의 헤더를 include합니다.

#include "clipplaneshaderclass.h"


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

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

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

ClipPlaneShaderClass 객체를 선언합니다.

    ClipPlaneShaderClass* m_ClipPlaneShader;
};

#endif

Graphicsclass.cpp

이전 튜토리얼에서 달라진 것들만 설명하겠습니다.

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


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

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

    m_ClipPlaneShader = 0;
}


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-&gt;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_Model = new ModelClass;
    if(!m_Model)
    {
        return false;
    }

삼각형 모델을 로드합니다.

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

클리핑 평면 셰이더 객체를 생성하고 초기화합니다.

    // 클리핑 평면 셰이더 객체를 생성합니다.
    m_ClipPlaneShader = new ClipPlaneShaderClass;
    if(!m_ClipPlaneShader)
    {
        return false;
    }

    // 생성한 객체를 초기화합니다.
    result = m_ClipPlaneShader-&gt;Initialize(m_D3D-&gt;GetDevice(), hwnd);
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize the clip plane shader object.", L"Error", MB_OK);
        return false;
    }

    return true;
}


void GraphicsClass::Shutdown()
{

Shutdown 함수에서 클리핑 평면 셰이더 객체를 해제합니다.

    // 클리핑 평면 셰이더를 해제합니다.
    if(m_ClipPlaneShader)
    {
        m_ClipPlaneShader-&gt;Shutdown();
        delete m_ClipPlaneShader;
        m_ClipPlaneShader = 0;
    }

    // 모델을 해제합니다.
    if(m_Model)
    {
        m_Model-&gt;Shutdown();
        delete m_Model;
        m_Model = 0;
    }

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

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

    return;
}


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

우선 클리핑 평면을 생성합니다. 이 평면은 Y값이 0 아래인 모든 것들을 그리지 않게 합니다.

    // 클리핑 평면을 설정합니다.
    clipPlane = D3DXVECTOR4(0.0f, -1.0f, 0.0f, 0.0f);

    // 렌더링을 시작하면서 버퍼를 클리어합니다.
    m_D3D-&gt;BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

    // 카메라의 위치를 근거로 뷰 행렬을 생성합니다.
    m_Camera-&gt;Render();

    // 카메라와 d3d객체에서 월드, 뷰, 프로젝션 행렬을 가져옵니다.
    m_D3D-&gt;GetWorldMatrix(worldMatrix);
    m_Camera-&gt;GetViewMatrix(viewMatrix);
    m_D3D-&gt;GetProjectionMatrix(projectionMatrix);

    // 렌더링을 위해 모델의 정점 및 인덱스 버퍼를 파이프라인에 추가합니다.
    m_Model-&gt;Render(m_D3D-&gt;GetDeviceContext());

클리핑 평면 셰이더를 이용해 삼각형을 그립니다. 위쪽 절반이 잘린 삼각형이 출력될 것입니다.

    // 클리핑 평면 셰이더를 이용하여 모델을 렌더링합니다.
    result = m_ClipPlaneShader-&gt;Render(m_D3D-&gt;GetDeviceContext(), m_Model-&gt;GetIndexCount(), worldMatrix, viewMatrix, 
                       projectionMatrix, m_Model-&gt;GetTexture(), clipPlane);
    if(!result)
    {
        return false;
    }

    // 렌더링이 끝난 화면을 출력합니다.
    m_D3D-&gt;EndScene();

    return true;
}

마치면서

이제 우리는 클리핑 평면을 이용하여 픽셀들을 컬링하는 수정된 텍스쳐 셰이더를 가지게 되었습니다.

연습문제

  1. 코드를 다시 컴파일하여 위쪽이 잘린 삼각형이 나오는지 확인해 보십시오. Esc 버튼으로 종료합니다.
  2. 삼각형 대신 육면체 모델을 사용하고 D3DClass에서 컬링을 끕니다. 육면체를 조금 회전시키고 카메라 위치도 조금 높여 봅니다. 육면체가 반으로 잘리고 그 안을 볼 수 있어야 합니다.
  3. 클리핑 평면을 바꾸어 다른 부분이 잘려지도록 해 보십시오.

소스코드

Visual Studio 2008 프로젝트: dx11tut24.zip

소스코드만: dx11src24.zip

실행파일: dx11exe24.zip

Nav