DirectX11 Tutorial 23: 안개

강좌번역/DirectX 11 2013. 9. 20. 19:58 by 빠재

Tutorial 23: Fog

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

이번 튜토리얼에서는 DirectX 11에서 HLSL과 C++를 이용하여 안개를 만드는 방법을 다룹니다. 코드는 이전 튜토리얼에서 이어집니다.

여기서는 가장 구현이 간편하며 기본적인 종류의 안개 효과를 설명할 것입니다. 첫 단계는 회색이나 그와 비슷한 색으로 안개의 색을 정하는 것입니다. 그리고 나서 그 색으로 백버퍼를 초기화합니다. 그렇게 되면 모든 화면이 안개가 낀 상태인 것처럼 시작하게 되는데, 그 다음에 안개 안에 있게 될 각 모델마다 픽셀 셰이더에서 안개 안의 위치에 따라 안개 색의 농도를 달리하여 더하게 하면 됩니다. 그 농도와 관련된 공식은 여러 가지가 있습니다. 이 튜토리얼에서는 가장 간단한 선형 함수를 사용할 것이지만 다른 것들도 지금 간단히 알아보겠습니다.


안개 공식

선형 안개 공식은 안개 안에서의 거리에 따라 농도를 선형적으로 다르게 하는 것입니다.

지수 안개 공식은 멀리 있을수록 지수적으로 농도를 짙게 하는 것입니다. 선형 함수에 비해 멀리 있는 물체가 잘 안보입니다.

두 번째 지수 안개 공식은 첫 지수공식보다 더 강하게 인자를 주기 때문에 매우 두꺼운 안개 효과를 낼 수 있습니다.

이 세 공식의 결과는 안개인자(fog factor)입니다. 모델의 텍스쳐에 안개 인자를 적용하고 최종 색상값을 뽑아내기 위해서 다음과 같은 공식을 사용합니다.

이 튜토리얼에서는 정점 셰이더에서 안개 인자를 계산하고 픽셀 셰이더에서 최종 색상값을 계산합니다.


프레임워크

프레임워크에 안개 렌더링을 위한 FogShaderClass라는 클래스가 추가됩니다. 이 클래스는 TextureShaderClass에 기반해 있으며 몇 가지 변경점이 있습니다.

언제나 그랬듯이 셰이더 코드부터 살펴보겠습니다.


Fog.vs

////////////////////////////////////////////////////////////////////////////////
// Filename: fog.vs
////////////////////////////////////////////////////////////////////////////////


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

안개 효과의 시작과 끝을 설정할 수 있도록 안개 상수 버퍼를 만듭니다.

cbuffer FogBuffer
{
    float fogStart;
    float fogEnd;
};


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

PixelInputType에 안개 인자를 받을 수 있도록 합니다(fogFactor). 안개 인자는 이 정점 셰이더에서 계산하고 픽셀 셰이더에 그 값을 넘겨줍니다.

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float fogFactor : FOG;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType FogVertexShader(VertexInputType input)
{
    PixelInputType output;
    float4 cameraPosition;
    

    // Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

    // Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    // Store the texture coordinates for the pixel shader.
    output.tex = input.tex;

뷰 공간상에서의 Z값을 계산합니다. 안개의 시작과 끝 위치 정보를 안개 인자 공식에 넣어 픽셀 셰이더에 전달할 안개 인자를 계산합니다.

    // Calculate the camera position.
    cameraPosition = mul(input.position, worldMatrix);
    cameraPosition = mul(cameraPosition, viewMatrix);

    // Calculate linear fog.    
    output.fogFactor = saturate((fogEnd - cameraPosition.z) / (fogEnd - fogStart));

    return output;
}

Fog.ps

////////////////////////////////////////////////////////////////////////////////
// Filename: fog.ps
////////////////////////////////////////////////////////////////////////////////


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


//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float fogFactor : FOG;
};


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 FogPixelShader(PixelInputType input) : SV_TARGET
{
    float4 textureColor;
    float4 fogColor;
    float4 finalColor;

    
    // Sample the texture pixel at this location.
    textureColor = shaderTexture.Sample(SampleType, input.tex);
    
    // Set the color of the fog to grey.
    fogColor = float4(0.5f, 0.5f, 0.5f, 1.0f);

안개 색상 공식은 안개 인자의 값을 이용하여 텍스쳐 색상과 안개 색상 간의 선형 보간을 합니다.

    // Calculate the final color using the fog effect equation.
    finalColor = input.fogFactor * textureColor + (1.0 - input.fogFactor) * fogColor;

    return finalColor;
}

Fogshaderclass.h

FogShaderClassTextureShaderClass와 같으나 안개를 표현하게 하기 위해 일부분을 고쳤습니다.

////////////////////////////////////////////////////////////////////////////////
// Filename: fogshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FOGSHADERCLASS_H_
#define _FOGSHADERCLASS_H_


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


////////////////////////////////////////////////////////////////////////////////
// Class name: FogShaderClass
////////////////////////////////////////////////////////////////////////////////
class FogShaderClass
{
private:
    struct ConstantBufferType
    {
        D3DXMATRIX world;
        D3DXMATRIX view;
        D3DXMATRIX projection;
    };

안개 버퍼 데이터를 위한 새로운 구조체를 만듭니다. 16바이트의 배수가 되게 하기 위해 8바이트의 padding을 붙입니다.

    struct FogBufferType
    {
        float fogStart;
        float fogEnd;
        float padding1, padding2;
    };

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

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

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

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

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

안개의 시작과 끝 정보를 셰이더에게 전달할 버퍼를 선언합니다.

    ID3D11Buffer* m_fogBuffer;
};

#endif

Fogshaderclass.cpp

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


FogShaderClass::FogShaderClass()
{
    m_vertexShader = 0;
    m_pixelShader = 0;
    m_layout = 0;
    m_constantBuffer = 0;
    m_sampleState = 0;

생성자에서 안개 버퍼 포인터를 초기화합니다.

    m_fogBuffer = 0;
}


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


FogShaderClass::~FogShaderClass()
{
}


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

fog.vs, fog.ps 셰이더 파일을 로드합니다.

    // Initialize the vertex and pixel shaders.
    result = InitializeShader(device, hwnd, L"../Engine/fog.vs", L"../Engine/fog.ps");
    if(!result)
    {
        return false;
    }

    return true;
}


void FogShaderClass::Shutdown()
{
    // Shutdown the vertex and pixel shaders as well as the related objects.
    ShutdownShader();

    return;
}

Render함수는 안개의 시작과 끝 정보를 인자로 받습니다. Render함수에서는 이 인자들을 셰이더에 설정하고 셰이더를 호출하여 실제 렌더링을 합니다.

bool FogShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
                D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, float fogStart, float fogEnd)
{
    bool result;


    // Set the shader parameters that it will use for rendering.
    result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, fogStart, fogEnd);
    if(!result)
    {
        return false;
    }

    // Now render the prepared buffers with the shader.
    RenderShader(deviceContext, indexCount);

    return true;
}


bool FogShaderClass::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 constantBufferDesc;
    D3D11_SAMPLER_DESC samplerDesc;
    D3D11_BUFFER_DESC fogBufferDesc;


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

안개 정점 셰이더를 호출합니다.

    // Compile the vertex shader code.
    result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "FogVertexShader", "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;
    }

안개 픽셀 셰이더를 호출합니다.

    // Compile the pixel shader code.
    result = D3DX11CompileFromFile(psFilename, NULL, NULL, "FogPixelShader", "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;
    }

    // Create the vertex shader from the buffer.
    result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, 
                        &m_vertexShader);
    if(FAILED(result))
    {
        return false;
    }

    // Create the vertex shader from the buffer.
    result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, 
                       &m_pixelShader);
    if(FAILED(result))
    {
        return false;
    }

    // Create the vertex input layout description.
    // This setup needs to match the VertexType stucture in the ModelClass and in the shader.
    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;

    // Get a count of the elements in the layout.
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

    // Create the vertex input layout.
    result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), 
                       vertexShaderBuffer->GetBufferSize(), &m_layout);
    if(FAILED(result))
    {
        return false;
    }

    // Release the vertex shader buffer and pixel shader buffer since they are no longer needed.
    vertexShaderBuffer->Release();
    vertexShaderBuffer = 0;

    pixelShaderBuffer->Release();
    pixelShaderBuffer = 0;

    // Setup the description of the dynamic constant buffer that is in the vertex shader.
    constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    constantBufferDesc.ByteWidth = sizeof(ConstantBufferType);
    constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    constantBufferDesc.MiscFlags = 0;
    constantBufferDesc.StructureByteStride = 0;

    // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
    result = device->CreateBuffer(&constantBufferDesc, NULL, &m_constantBuffer);
    if(FAILED(result))
    {
        return false;
    }

    // Create a texture sampler state 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;

    // Create the texture sampler state.
    result = device->CreateSamplerState(&samplerDesc, &m_sampleState);
    if(FAILED(result))
    {
        return false;
    }

안개 버퍼의 description을 작성하고 동적 안개 상수 버퍼를 생성합니다.

    // Setup the description of the dynamic fog constant buffer that is in the vertex shader.
    fogBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    fogBufferDesc.ByteWidth = sizeof(FogBufferType);
    fogBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    fogBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    fogBufferDesc.MiscFlags = 0;
    fogBufferDesc.StructureByteStride = 0;

    // Create the fog buffer pointer so we can access the vertex shader fog constant buffer from within this class.
    result = device->CreateBuffer(&fogBufferDesc, NULL, &m_fogBuffer);
    if(FAILED(result))
    {
        return false;
    }

    return true;
}


void FogShaderClass::ShutdownShader()
{

ShutdownShader함수에서 안개 버퍼를 해제합니다.

    // Release the fog constant buffer.
    if(m_fogBuffer)
    {
        m_fogBuffer->Release();
        m_fogBuffer = 0;
    }

    // Release the sampler state.
    if(m_sampleState)
    {
        m_sampleState->Release();
        m_sampleState = 0;
    }

    // Release the constant buffer.
    if(m_constantBuffer)
    {
        m_constantBuffer->Release();
        m_constantBuffer = 0;
    }

    // Release the layout.
    if(m_layout)
    {
        m_layout->Release();
        m_layout = 0;
    }

    // Release the pixel shader.
    if(m_pixelShader)
    {
        m_pixelShader->Release();
        m_pixelShader = 0;
    }

    // Release the vertex shader.
    if(m_vertexShader)
    {
        m_vertexShader->Release();
        m_vertexShader = 0;
    }

    return;
}


void FogShaderClass::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 FogShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
                     D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, float fogStart, 
                     float fogEnd)
{
    HRESULT result;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    ConstantBufferType* dataPtr;
    unsigned int bufferNumber;
    FogBufferType* dataPtr2;


    // Lock the constant buffer so it can be written to.
    result = deviceContext->Map(m_constantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // Get a pointer to the data in the constant buffer.
    dataPtr = (ConstantBufferType*)mappedResource.pData;

    // Transpose the matrices to prepare them for the shader.
    D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
    D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
    D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);

    // Copy the matrices into the constant buffer.
    dataPtr->world = worldMatrix;
    dataPtr->view = viewMatrix;
    dataPtr->projection = projectionMatrix;

    // Unlock the constant buffer.
    deviceContext->Unmap(m_constantBuffer, 0);

    // Set the position of the constant buffer in the vertex shader.
    bufferNumber = 0;

    // Now set the constant buffer in the vertex shader with the updated values.
    deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_constantBuffer);

    // Set shader texture resource in the pixel shader.
    deviceContext->PSSetShaderResources(0, 1, &texture);

버퍼를 잠그고, 데이터를 설정하고, 잠금을 해제하는 방식으로 안개의 시작과 끝 정보를 버퍼에 씁니다. 이 버퍼는 정점 셰이더상에서 2번째 버퍼이므로 bufferNumber를 1로 합니다.

    // Lock the fog constant buffer so it can be written to.
    result = deviceContext->Map(m_fogBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // Get a pointer to the data in the constant buffer.
    dataPtr2 = (FogBufferType*)mappedResource.pData;

    // Copy the fog information into the fog constant buffer.
    dataPtr2->fogStart = fogStart;
    dataPtr2->fogEnd = fogEnd;

    // Unlock the constant buffer.
    deviceContext->Unmap(m_fogBuffer, 0);

    // Set the position of the fog constant buffer in the vertex shader.
    bufferNumber = 1;

    // Now set the fog buffer in the vertex shader with the updated values.
    deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_fogBuffer);

    return true;
}


void FogShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
    // Set the vertex input layout.
    deviceContext->IASetInputLayout(m_layout);

    // Set the vertex and pixel shaders that will be used to render this triangle.
    deviceContext->VSSetShader(m_vertexShader, NULL, 0);
    deviceContext->PSSetShader(m_pixelShader, NULL, 0);

    // Set the sampler state in the pixel shader.
    deviceContext->PSSetSamplers(0, 1, &m_sampleState);

    // Render the triangle.
    deviceContext->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"

FogShaderClass의 헤더 파일을 포함시킵니다.

#include "fogshaderclass.h"


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

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

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

FogShaderClass객체를 선언합니다.

    FogShaderClass* m_FogShader;
};

#endif

Graphicsclass.cpp

이전 튜토리얼과 다른 부분을 위주로 설명하겠습니다.

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


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

FogShaderClass객체를 초기화합니다.

    m_FogShader = 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;
    }

    // Create the model object.
    m_Model = new ModelClass;
    if(!m_Model)
    {
        return false;
    }

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

FogShaderClass객체를 생성하고 초기화합니다.

    // Create the fog shader object.
    m_FogShader = new FogShaderClass;
    if(!m_FogShader)
    {
        return false;
    }

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

    return true;
}


void GraphicsClass::Shutdown()
{

Shutdown 함수에서 FogShaderClass 객체를 해제합니다.

    // Release the fog shader object.
    if(m_FogShader)
    {
        m_FogShader->Shutdown();
        delete m_FogShader;
        m_FogShader = 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::Render()
{
    float fogColor, fogStart, fogEnd;
    D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
    bool result;
    static float rotation = 0.0f;

안개와 관련된 인자들을 초기화합니다.

    // Set the color of the fog to grey.
    fogColor = 0.5f;

    // Set the start and end of the fog.
    fogStart = 0.0f;
    fogEnd = 10.0f;

백버퍼를 안개의 색상으로 지우는데 이 단계는 이 안개의 구현에 있어서 매우 중요한 부분입니다.

    // Clear the buffers to begin the scene.
    m_D3D->BeginScene(fogColor, fogColor, fogColor, 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_D3D->GetWorldMatrix(worldMatrix);
    m_Camera->GetViewMatrix(viewMatrix);
    m_D3D->GetProjectionMatrix(projectionMatrix);

    // Update the rotation variable each frame.
    rotation += (float)D3DX_PI * 0.005f;
    if(rotation > 360.0f)
    {
        rotation -= 360.0f;
    }

    // Multiply the world matrix by the rotation.
    D3DXMatrixRotationY(&worldMatrix, rotation);

    // 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 with the fog shader.
    result = m_FogShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, 
                     m_Model->GetTexture(), fogStart, fogEnd);
    if(!result)
    {
        return false;
    }

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

    return true;
}

마치면서

여러 안개 공식에 대해서 살펴보았고 그 중에서도 가장 간단한 선형 안개 효과를 내는 법을 알아보았습니다. 만약 다른 공식을 적용해본다면 그 차이를 확인해 보기 위해서는 나무가 몇 개 있는 것과 같은 큰 장면을 이용하는 것이 좋을 것입니다.

연습 문제

  1. 프로그램을 다시 컴파일하고 실행해 보십시오. 회전하는 육면체가 안개 속에 있는 것이 보일 것입니다. esc키를 눌러 종료합니다.
  2. 안개의 시작과 끝 위치 값을 바꿔 보십시오.
  3. 백버퍼 클리어 색상을 안개 색 대신 검은색으로 하여 실제로 모델에 어떤 효과가 일어나는지 보십시오.
  4. 앞서 설명했던 다른 두 개의 안개 공식을 사용해 보십시오.

소스 코드

Visual Studio 2008 프로젝트: dx11tut23.zip

소스 코드: dx11src23.zip

실행 파일: dx11exe23.zip

Nav