원문: http://rastertek.com/dx11tut28.html (이 번역에 나오는 모든 이미지와 소스코드의 출처는 원문입니다.)
이번 튜토리얼에서는 페이드 이펙트를 만드는 내용을 다룰 것입니다. 이 튜토리얼은 이전 튜토리얼에서 이어지는 것이며 DirectX11 및 HLSL과 C++을 사용합니다.
프로그램의 품질을 높이는 방법 중 효과적인 것 하나는 화면 간의 전환이 있을 때 재빠른 페이드 이펙트를 주는 것입니다. 로딩 화면을 보여줬다가 불쑥 다음 장면을 보여주던 전통적인 방법은 개선되어야 마땅합니다. 화면 페이딩 효과를 만드는 가장 간단한 방법 중 하나는 렌더 투 텍스쳐 기능을 이용하여 픽셀들의 색상을 점점 바꾸는 것입니다. 그 외에도 두 장면을 그려서 미끄러지듯 지나가게 한다던지 다른 녹아드는듯한 방법 등 다양하게 확장할 수 있습니다.
이 튜토리얼에서는 렌더 투 텍스쳐 기능을 이용하여 페이드 효과를 만들 것입니다. 화면이 100% 가려질 때 다음 화면을 렌더링하는 것으로 바꾸어 렌더 투 텍스쳐를 끌 것입니다.
프레임워크
이번 프레임워크에는 페이드 인 효과를 처리하는 FadeShaderClass
라는 클래스가 들어갑니다. 그리고 BitmapClass
를 다시 사용하여 FadeShaderClass
와 RenderTextureClass
와 같이 동작할 수 있도록 하였습니다. 페이드 인 연출 시간을 다루기 위한 TimerClass
도 사용됩니다.
Fade.vs
페이드 HLSL 셰이더는 기존 텍스쳐 셰이더를 조금 바꾼 것입니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: fade.vs
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};
//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
};
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType FadeVertexShader(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;
return output;
}
Fade.ps
////////////////////////////////////////////////////////////////////////////////
// Filename: fade.ps
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
Texture2D shaderTexture;
SamplerState SampleType;
FadeBuffer
이라는 버퍼를 추가하여 fadeAmount
로 텍스쳐의 색상 강도를 조절할 수 있도록 합니다. 이 값은 매 프레임마다 증가하며 이미지를 밝아지게 하므로 페이드 인 효과가 만들어집니다. 값의 범위는 0.0(0%)에서 1.0(100%)까지입니다. padding
변수는 버퍼의 크기가 16의 배수가 되도록 잡아놓은 것입니다.
cbuffer FadeBuffer
{
float fadeAmount;
float3 padding;
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 FadePixelShader(PixelInputType input) : SV_TARGET
{
float4 color;
// 텍스쳐의 현재 위치 색상을 샘플링합니다.
color = shaderTexture.Sample(SampleType, input.tex);
fadeAmount
를 사용하여 출력되는 모든 픽셀의 밝기가 특정한 값이 되도록 합니다.
// 색상의 밝기를 현재 페이드 퍼센트에 맞게 줄입니다.
color = color * fadeAmount;
return color;
}
Fadeshaderclass.h
FadeShaderClass
는 TextureShaderClass
가 텍스쳐 페이드 효과를 지원하도록 수정한 것입니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: fadeshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FADESHADERCLASS_H_
#define _FADESHADERCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <d3dx11async.h>
#include <fstream>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// Class name: FadeShaderClass
////////////////////////////////////////////////////////////////////////////////
class FadeShaderClass
{
private:
struct MatrixBufferType
{
D3DXMATRIX world;
D3DXMATRIX view;
D3DXMATRIX projection;
};
픽셀 셰이더의 페이드 상수 버퍼에 해당하는 구조체입니다.
struct FadeBufferType
{
float fadeAmount;
D3DXVECTOR3 padding;
};
public:
FadeShaderClass();
FadeShaderClass(const FadeShaderClass&);
~FadeShaderClass();
bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, float);
private:
bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*);
void ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, float);
void RenderShader(ID3D11DeviceContext*, int);
private:
ID3D11VertexShader* m_vertexShader;
ID3D11PixelShader* m_pixelShader;
ID3D11InputLayout* m_layout;
ID3D11Buffer* m_matrixBuffer;
ID3D11SamplerState* m_sampleState;
m_fadeBuffer
은 텍스쳐의 모든 픽셀들의 밝기를 조절하는 데 사용됩니다.
ID3D11Buffer* m_fadeBuffer;
};
#endif
Fadeshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: fadeshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "fadeshaderclass.h"
FadeShaderClass::FadeShaderClass()
{
m_vertexShader = 0;
m_pixelShader = 0;
m_layout = 0;
m_matrixBuffer = 0;
m_sampleState = 0;
새로 만든 버퍼를 생성자에서 null로 초기화합니다.
m_fadeBuffer = 0;
}
FadeShaderClass::FadeShaderClass(const FadeShaderClass& other)
{
}
FadeShaderClass::~FadeShaderClass()
{
}
bool FadeShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
bool result;
fade.vs
및 fade.ps
HLSL 셰이더 코드를 로드합니다.
// 정점 및 픽셀 셰이더를 초기화합니다.
result = InitializeShader(device, hwnd, L"../Engine/fade.vs", L"../Engine/fade.ps");
if(!result)
{
return false;
}
return true;
}
void FadeShaderClass::Shutdown()
{
// Shutdown the vertex and pixel shaders as well as the related objects.
ShutdownShader();
return;
}
Render
함수는 faceAmount
라는 인자를 받습니다. 이 인자는 텍스쳐가 그려야 할 밝기의 퍼센트 값을 가집니다. 텍스쳐에 그려지기 전에 이 값이 셰이더에 지정됩니다.
bool FadeShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, float fadeAmount)
{
bool result;
// 렌더링에 사용할 셰이더 인자들을 설정합니다.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, fadeAmount);
if(!result)
{
return false;
}
// 셰이더를 이용하여 준비된 버퍼에 그려냅니다.
RenderShader(deviceContext, indexCount);
return true;
}
bool FadeShaderClass::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 fadeBufferDesc;
// 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, "FadeVertexShader", "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, "FadePixelShader", "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 matrix dynamic constant buffer that is in the vertex shader.
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;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
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;
}
픽셀 셰이더 안의 fadeAmount
값을 저장할 버퍼를 생성합니다.
// Setup the description of the fade dynamic constant buffer that is in the vertex shader.
fadeBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
fadeBufferDesc.ByteWidth = sizeof(FadeBufferType);
fadeBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
fadeBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
fadeBufferDesc.MiscFlags = 0;
fadeBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the pixel shader constant buffer from within this class.
result = device->CreateBuffer(&fadeBufferDesc, NULL, &m_fadeBuffer);
if(FAILED(result))
{
return false;
}
return true;
}
void FadeShaderClass::ShutdownShader()
{
ShutdownShader
함수에서 페이드 버퍼를 해제합니다.
// 상수 버퍼를 해제합니다.
if(m_fadeBuffer)
{
m_fadeBuffer->Release();
m_fadeBuffer = 0;
}
// Release the sampler state.
if(m_sampleState)
{
m_sampleState->Release();
m_sampleState = 0;
}
// Release the matrix constant buffer.
if(m_matrixBuffer)
{
m_matrixBuffer->Release();
m_matrixBuffer = 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 FadeShaderClass::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 FadeShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, float fadeAmount)
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;
FadeBufferType* dataPtr2;
// Transpose the matrices to prepare them for the shader.
D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);
// Lock the matrix constant buffer so it can be written to.
result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the matrix constant buffer.
dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the matrix constant buffer.
dataPtr->world = worldMatrix;
dataPtr->view = viewMatrix;
dataPtr->projection = projectionMatrix;
// Unlock the matrix constant buffer.
deviceContext->Unmap(m_matrixBuffer, 0);
// Set the position of the matrix constant buffer in the vertex shader.
bufferNumber = 0;
// Now set the matrix constant buffer in the vertex shader with the updated values.
deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);
// Set shader texture resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 1, &texture);
여기서 픽셀 셰이더에 fadeAmount
값을 설정합니다. 우선 페이드 버퍼를 잠근 다음 갱신할 fadeAmount
값을 복사해 넣습니다. 그런 뒤에 페이드 버퍼의 잠금을 풀고 PSSetConstantBuffers
함수를 통하여 픽셀 셰이더에 적용합니다.
// 상수 버퍼를 잠궈 내용을 쓸 수 있도록 합니다.
result = deviceContext->Map(m_fadeBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// 페이드 상수 버퍼의 데이터를 가리키는 포인터를 얻습니다.
dataPtr2 = (FadeBufferType*)mappedResource.pData;
// fadeAmount의 값을 버퍼로 복사합니다.
dataPtr2->fadeAmount = fadeAmount;
dataPtr2->padding = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
// 상수 버퍼의 잠금을 해제합니다.
deviceContext->Unmap(m_fadeBuffer, 0);
// 픽셀 셰이더에서의 상수 버퍼의 위치를 설정합니다.
bufferNumber = 0;
// 픽셀 셰이더의 페이드 상수 버퍼를 새로운 값으로 갱신합니다.
deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_fadeBuffer);
return true;
}
void FadeShaderClass::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;
}
Bitmapclass.h
BitmapClass
는 이전의 구현에서 아무런 텍스쳐의 정보도 가지지 않도록 수정되었습니다. 이전에 BitmapClass
가 실제 텍스쳐를 제공했던 것 대신 지금은 FadeShaderClass
가 RenderTextureClass
에 실제 텍스쳐를 제공합니다. 하지만 렌더 투 텍스쳐를 비트맵처럼 2D 화면에 그리는 기능이 필요하기 때문에 이 수정된 BitmapClass
에서 버퍼를 준비하고 페이드 셰이더가 호출되도록 할 것입니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: bitmapclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _BITMAPCLASS_H_
#define _BITMAPCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
////////////////////////////////////////////////////////////////////////////////
// Class name: BitmapClass
////////////////////////////////////////////////////////////////////////////////
class BitmapClass
{
private:
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR2 texture;
};
public:
BitmapClass();
BitmapClass(const BitmapClass&);
~BitmapClass();
bool Initialize(ID3D11Device*, int, int, int, int);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, int);
int GetIndexCount();
private:
bool InitializeBuffers(ID3D11Device*);
void ShutdownBuffers();
bool UpdateBuffers(ID3D11DeviceContext*, int, int);
void RenderBuffers(ID3D11DeviceContext*);
bool LoadTexture(ID3D11Device*, WCHAR*);
void ReleaseTexture();
private:
ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
int m_vertexCount, m_indexCount;
int m_screenWidth, m_screenHeight;
int m_bitmapWidth, m_bitmapHeight;
int m_previousPosX, m_previousPosY;
};
#endif
Bitmapclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: bitmapclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "bitmapclass.h"
BitmapClass::BitmapClass()
{
m_vertexBuffer = 0;
m_indexBuffer = 0;
}
BitmapClass::BitmapClass(const BitmapClass& other)
{
}
BitmapClass::~BitmapClass()
{
}
bool BitmapClass::Initialize(ID3D11Device* device, int screenWidth, int screenHeight, int bitmapWidth, int bitmapHeight)
{
bool result;
// Store the screen size.
m_screenWidth = screenWidth;
m_screenHeight = screenHeight;
// Store the size in pixels that this bitmap should be rendered at.
m_bitmapWidth = bitmapWidth;
m_bitmapHeight = bitmapHeight;
// Initialize the previous rendering position to -1.
m_previousPosX = -1;
m_previousPosY = -1;
// Initialize the vertex and index buffers.
result = InitializeBuffers(device);
if(!result)
{
return false;
}
return true;
}
void BitmapClass::Shutdown()
{
// Shutdown the vertex and index buffers.
ShutdownBuffers();
return;
}
bool BitmapClass::Render(ID3D11DeviceContext* deviceContext, int positionX, int positionY)
{
bool result;
// Re-build the dynamic vertex buffer for rendering to possibly a different location on the screen.
result = UpdateBuffers(deviceContext, positionX, positionY);
if(!result)
{
return false;
}
// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
RenderBuffers(deviceContext);
return true;
}
int BitmapClass::GetIndexCount()
{
return m_indexCount;
}
bool BitmapClass::InitializeBuffers(ID3D11Device* device)
{
VertexType* vertices;
unsigned long* indices;
D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
D3D11_SUBRESOURCE_DATA vertexData, indexData;
HRESULT result;
int i;
// Set the number of vertices in the vertex array.
m_vertexCount = 6;
// Set the number of indices in the index array.
m_indexCount = m_vertexCount;
// Create the vertex array.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
return false;
}
// Create the index array.
indices = new unsigned long[m_indexCount];
if(!indices)
{
return false;
}
// Initialize vertex array to zeros at first.
memset(vertices, 0, (sizeof(VertexType) * m_vertexCount));
// Load the index array with data.
for(i=0; i<m_indexCount; i++)
{
indices[i] = i;
}
// Set up the description of the static vertex buffer.
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the vertex data.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
// Now create the vertex buffer.
result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
if(FAILED(result))
{
return false;
}
// Set up the description of the static index buffer.
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;
// Give the subresource structure a pointer to the index data.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// Create the index buffer.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
if(FAILED(result))
{
return false;
}
// Release the arrays now that the vertex and index buffers have been created and loaded.
delete [] vertices;
vertices = 0;
delete [] indices;
indices = 0;
return true;
}
void BitmapClass::ShutdownBuffers()
{
// Release the index buffer.
if(m_indexBuffer)
{
m_indexBuffer->Release();
m_indexBuffer = 0;
}
// Release the vertex buffer.
if(m_vertexBuffer)
{
m_vertexBuffer->Release();
m_vertexBuffer = 0;
}
return;
}
bool BitmapClass::UpdateBuffers(ID3D11DeviceContext* deviceContext, int positionX, int positionY)
{
float left, right, top, bottom;
VertexType* vertices;
D3D11_MAPPED_SUBRESOURCE mappedResource;
VertexType* verticesPtr;
HRESULT result;
// If the position we are rendering this bitmap to has not changed then don't update the vertex buffer since it
// currently has the correct parameters.
if((positionX == m_previousPosX) && (positionY == m_previousPosY))
{
return true;
}
// If it has changed then update the position it is being rendered to.
m_previousPosX = positionX;
m_previousPosY = positionY;
// Calculate the screen coordinates of the left side of the bitmap.
left = (float)((m_screenWidth / 2) * -1) + (float)positionX;
// Calculate the screen coordinates of the right side of the bitmap.
right = left + (float)m_bitmapWidth;
// Calculate the screen coordinates of the top of the bitmap.
top = (float)(m_screenHeight / 2) - (float)positionY;
// Calculate the screen coordinates of the bottom of the bitmap.
bottom = top - (float)m_bitmapHeight;
// Create the vertex array.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
return false;
}
// Load the vertex array with data.
// First triangle.
vertices[0].position = D3DXVECTOR3(left, top, 0.0f); // Top left.
vertices[0].texture = D3DXVECTOR2(0.0f, 0.0f);
vertices[1].position = D3DXVECTOR3(right, bottom, 0.0f); // Bottom right.
vertices[1].texture = D3DXVECTOR2(1.0f, 1.0f);
vertices[2].position = D3DXVECTOR3(left, bottom, 0.0f); // Bottom left.
vertices[2].texture = D3DXVECTOR2(0.0f, 1.0f);
// Second triangle.
vertices[3].position = D3DXVECTOR3(left, top, 0.0f); // Top left.
vertices[3].texture = D3DXVECTOR2(0.0f, 0.0f);
vertices[4].position = D3DXVECTOR3(right, top, 0.0f); // Top right.
vertices[4].texture = D3DXVECTOR2(1.0f, 0.0f);
vertices[5].position = D3DXVECTOR3(right, bottom, 0.0f); // Bottom right.
vertices[5].texture = D3DXVECTOR2(1.0f, 1.0f);
// Lock the vertex buffer so it can be written to.
result = deviceContext->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the vertex buffer.
verticesPtr = (VertexType*)mappedResource.pData;
// Copy the data into the vertex buffer.
memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * m_vertexCount));
// Unlock the vertex buffer.
deviceContext->Unmap(m_vertexBuffer, 0);
// Release the vertex array as it is no longer needed.
delete [] vertices;
vertices = 0;
return true;
}
void BitmapClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
unsigned int stride;
unsigned int offset;
// Set vertex buffer stride and offset.
stride = sizeof(VertexType);
offset = 0;
// Set the vertex buffer to active in the input assembler so it can be rendered.
deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
// Set the index buffer to active in the input assembler so it can be rendered.
deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
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 "textureshaderclass.h"
#include "rendertextureclass.h"
#include "bitmapclass.h"
FadeShaderClass
의 헤더 파일을 추가합니다.
#include "fadeshaderclass.h"
////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();
bool Initialize(int, int, HWND);
void Shutdown();
bool Frame(float);
bool Render();
private:
bool RenderToTexture(float);
bool RenderFadingScene();
bool RenderNormalScene(float);
private:
D3DClass* m_D3D;
CameraClass* m_Camera;
ModelClass* m_Model;
TextureShaderClass* m_TextureShader;
RenderTextureClass* m_RenderTexture;
BitmapClass* m_Bitmap;
페이드 인 효과에 대한 정보를 담는 변수를 추가합니다.
float m_fadeInTime, m_accumulatedTime, m_fadePercentage;
bool m_fadeDone;
FadeShaderClass
객체를 선언합니다.
FadeShaderClass* m_FadeShader;
};
#endif
Graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"
GraphicsClass::GraphicsClass()
{
m_D3D = 0;
m_Camera = 0;
m_Model = 0;
m_TextureShader = 0;
m_RenderTexture = 0;
m_Bitmap = 0;
FadeShaderClass
객체를 null로 초기화합니다.
m_FadeShader = 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;
}
// Create the texture shader object.
m_TextureShader = new TextureShaderClass;
if(!m_TextureShader)
{
return false;
}
// Initialize the texture shader object.
result = m_TextureShader->Initialize(m_D3D->GetDevice(), hwnd);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the texture shader object.", L"Error", MB_OK);
return false;
}
렌더 투 텍스쳐 객체를 생성 및 초기화합니다.
// 렌더 투 텍스쳐 객체를 생성합니다.
m_RenderTexture = new RenderTextureClass;
if(!m_RenderTexture)
{
return false;
}
// 렌더 투 텍스쳐 객체를 초기화합니다.
result = m_RenderTexture->Initialize(m_D3D->GetDevice(), screenWidth, screenHeight);
if(!result)
{
return false;
}
비트맵 객체를 생성 및 초기화합니다. 화면 전체에 그릴 것이므로 크기는 화면의 크기만큼 설정합니다.
// 비트맵 객체를 생성합니다.
m_Bitmap = new BitmapClass;
if(!m_Bitmap)
{
return false;
}
// 비트맵 객체를 초기화합니다.
result = m_Bitmap->Initialize(m_D3D->GetDevice(), screenWidth, screenHeight, screenWidth, screenHeight);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the bitmap object.", L"Error", MB_OK);
return false;
}
페이드 변수들을 설정합니다.
// 페이드 인 시간을 3000ms로 설정합니다.
m_fadeInTime = 3000.0f;
// 누적 시간을 0ms로 초기화합니다.
m_accumulatedTime = 0;
// 페이드 진행률을 0%로 하여 화면이 검게 나오도록 합니다.
m_fadePercentage = 0;
// 페이드 이펙트가 끝나지 않은 것으로 표시합니다.
m_fadeDone = false;
페이드 셰이더 객체를 생성 및 초기화합니다.
// 페이드 셰이더 객체를 생성합니다.
m_FadeShader = new FadeShaderClass;
if(!m_FadeShader)
{
return false;
}
// 페이드 셰이더 객체를 초기화합니다.
result = m_FadeShader->Initialize(m_D3D->GetDevice(), hwnd);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the fade shader object.", L"Error", MB_OK);
return false;
}
return true;
}
void GraphicsClass::Shutdown()
{
Shutdown
함수에서 FadeShaderClass
객체를 해제합니다.
// 페이드 셰이더 객체를 해제합니다.
if(m_FadeShader)
{
m_FadeShader->Shutdown();
delete m_FadeShader;
m_FadeShader = 0;
}
// 비트맵 객체를 해제합니다.
if(m_Bitmap)
{
m_Bitmap->Shutdown();
delete m_Bitmap;
m_Bitmap = 0;
}
// 렌더 투 텍스쳐 객체를 해제합니다.
if(m_RenderTexture)
{
m_RenderTexture->Shutdown();
delete m_RenderTexture;
m_RenderTexture = 0;
}
// 텍스쳐 셰이더 객체를 해제합니다.
if(m_TextureShader)
{
m_TextureShader->Shutdown();
delete m_TextureShader;
m_TextureShader = 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;
}
Frame
함수는 페이드 인 효과의 진행을 관장합니다. 매 프레임마다 프레임 간 시간이 함수의 인자로 전달되고 텍스쳐가 어느 만큼 보여야 할 지 계산됩니다. 누적된 프레임 시간이 페이드 인 시간을 넘게 되면 m_fadeDone
변수를 true
로 바꾸어 더 이상 페이드 효과가 나지 않고 일반 장면을 그리도록 합니다.
bool GraphicsClass::Frame(float frameTime)
{
if(!m_fadeDone)
{
// 누적 시간을 추가합니다.
m_accumulatedTime += frameTime;
// 시간이 지나면서 페이드 효과의 진행률이 증가하도록 합니다.
if(m_accumulatedTime < m_fadeInTime)
{
// 누적된 시간을 이용하여 어느정도까지 진행되었는지 계산합니다.
m_fadePercentage = m_accumulatedTime / m_fadeInTime;
}
else
{
// 페이드 인 시간이 지난 경우 페이드 효과를 끄고 일반 장면을 그리도록 합니다.
m_fadeDone = true;
// 진행률을 100%로 합니다.
m_fadePercentage = 1.0f;
}
}
// 카메라의 위치를 설정합니다.
m_Camera->SetPosition(0.0f, 0.0f, -10.0f);
return true;
}
Render
함수는 육면체의 회전뿐만 아니라 렌더 투 텍스쳐와 페이드 인 효과의 전환을 담당하며 페이드 효과가 끝나면 일반 장면을 그리는 기능을 담당합니다.
bool GraphicsClass::Render()
{
bool result;
static float rotation = 0.0f;
// 매 프레임마다 회전량을 갱신합니다.
rotation += (float)D3DX_PI * 0.005f;
if(rotation > 360.0f)
{
rotation -= 360.0f;
}
if(m_fadeDone)
{
// 페이드 효과가 끝나면 백버퍼에 일반 장면을 그립니다.
RenderNormalScene(rotation);
}
else
{
// 페이드 효과가 끝나지 않았다면 렌더 투 텍스쳐로 그려낸 뒤 그 텍스쳐에 페이드 효과를 줍니다.
result = RenderToTexture(rotation);
if(!result)
{
return false;
}
result = RenderFadingScene();
if(!result)
{
return false;
}
}
return true;
}
RenderToTexture
함수는 회전하는 육면체를 백버퍼가 아닌 텍스쳐에 그리도록 합니다.
bool GraphicsClass::RenderToTexture(float rotation)
{
D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
bool result;
// 렌더 테겟을 렌더 투 텍스쳐가 되게 합니다.
m_RenderTexture->SetRenderTarget(m_D3D->GetDeviceContext(), m_D3D->GetDepthStencilView());
// 스텐실 뷰의 내용을 초기화합니다.
m_RenderTexture->ClearRenderTarget(m_D3D->GetDeviceContext(), m_D3D->GetDepthStencilView(), 0.0f, 0.0f, 0.0f, 1.0f);
// 카메라의 위치를 이용하여 뷰 행렬을 생성합니다.
m_Camera->Render();
// 카메라 및 d3d객체에서 월드, 뷰, 투영 행렬을 구합니다.
m_D3D->GetWorldMatrix(worldMatrix);
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
// 월드 행렬에 회전을 곱합니다.
D3DXMatrixRotationY(&worldMatrix, rotation);
// 렌더링이 일어나기 전에 모델의 정점 및 인덱스 버퍼를 파이프라인에 넣습니다.
m_Model->Render(m_D3D->GetDeviceContext());
// 텍스쳐 셰이더를 이용하여 모델을 그려냅니다.
result = m_TextureShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix,
projectionMatrix, m_Model->GetTexture());
if(!result)
{
return false;
}
// 렌더 타겟을 다시 원래의 백버퍼로 돌려놓습니다.
m_D3D->SetBackBufferRenderTarget();
return true;
}
RenderFadingScene
함수는 렌더 투 텍스쳐로 뽑힌 육면체 이미지를 FadeShaderClass
를 통하여 마치 2D 비트맵처럼 화면에 그립니다. FadeShaderClass
객체는 m_fadePercentage
의 값을 이용하여 매 프레임마다 어느 정도의 밝기가 들어가야 하는지 알아냅니다.
bool GraphicsClass::RenderFadingScene()
{
D3DXMATRIX worldMatrix, viewMatrix, orthoMatrix;
bool result;
// 화면을 그리기 위하여 내용을 초기화합니다.
m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// 카메라의 위치를 이용하여 뷰 행렬을 생성합니다.
m_Camera->Render();
// 카메라와 d3d 객체에서 월드, 뷰, 직교 행렬을 구합니다.
m_D3D->GetWorldMatrix(worldMatrix);
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetOrthoMatrix(orthoMatrix);
// 2D 렌더링이므로 Z버퍼를 끕니다.
m_D3D->TurnZBufferOff();
// 렌더링이 일어나기 전에 비트맵의 정점 및 인덱스 버퍼를 파이프라인에 넣습니다.
result = m_Bitmap->Render(m_D3D->GetDeviceContext(), 0, 0);
if(!result)
{
return false;
}
// 페이드 셰이더를 이용하여 비트맵을 그립니다.
result = m_FadeShader->Render(m_D3D->GetDeviceContext(), m_Bitmap->GetIndexCount(), worldMatrix, viewMatrix, orthoMatrix,
m_RenderTexture->GetShaderResourceView(), m_fadePercentage);
if(!result)
{
return false;
}
// 2D 렌더링이 끝나면 Z버퍼를 다시 켜줍니다.
m_D3D->TurnZBufferOn();
// 그려진 화면을 스크린에 표시합니다.
m_D3D->EndScene();
return true;
}
페이드 효과가 끝났다면 이 함수를 호출하여 렌더 투 텍스쳐를 통하지 않고 평소대로 큰 부하 없이 장면을 그릴 수 있도록 합니다.
bool GraphicsClass::RenderNormalScene(float rotation)
{
D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
bool result;
// 장면의 내용을 초기화합니다.
m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// 카메라의 위치에서 뷰 행렬을 구합니다.
m_Camera->Render();
// 카메라와 d3d객체에서 월드, 뷰, 투영 행렬을 구합니다.
m_D3D->GetWorldMatrix(worldMatrix);
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
// 월드 행렬에 회전을 곱합니다.
D3DXMatrixRotationY(&worldMatrix, rotation);
// 렌더링이 일어나기 전에 모델의 정점 및 인덱스 버퍼를 파이프라인에 넣습니다.
m_Model->Render(m_D3D->GetDeviceContext());
// 텍스쳐 셰이더를 이용하여 모델을 그립니다.
result = m_TextureShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix,
projectionMatrix, m_Model->GetTexture());
if(!result)
{
return false;
}
// 그려진 화면을 스크린에 표시합니다.
m_D3D->EndScene();
return true;
}
마치면서
렌더 투 텍스쳐를 이용하여 3D 장면에서 페이드 인 및 페이드 아웃 효과를 낼 수 있습니다. 여기에 이전 화면과 새로운 화면을 동시에 그리는 식의 더욱 멋진 전환 효과로 확장할 수 있습니다.
연습문제
프로그램을 다시 컴파일하고 실행해 보십시오. 육면체가 5초간 페이드 인 된 다음 평소대로 그려질 것입니다. esc키로 종료합니다.
페이드 인 시간을 바꾸어 보십시오.
백버퍼의 클리어 색상을 바꾸어 렌더 투 텍스쳐에서 평소대로 렌더링이 전환되는 시점이 언제인지 확인해 보십시오.
코드를 고쳐 페이드 인이 끝난 다음 페이드 아웃이 되도록 해 보십시오.
소스코드
Visual Studio 2008 프로젝트: dx11tut28.zip
소스코드만: dx11src28.zip
실행파일만: dx11exe28.zip
'강좌번역 > DirectX 11' 카테고리의 다른 글
DirectX11 Tutorial 30: 다중 점조명 (0) | 2017.09.03 |
---|---|
DirectX11 Tutorial 29: 물 (2) | 2017.09.01 |
DirectX11 Tutorial 27: 반사 (0) | 2015.06.21 |
DirectX11 Tutorial 26: 투명도 (1) | 2015.06.14 |
DirectX11 Tutorial 25: 텍스쳐 이동 (0) | 2015.06.07 |