원문 : http://www.rastertek.com/dx11tut17.html
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
Tutorial 17: Multitexturing and Texture Arrays
이번 강좌는 DirectX 11 에서 멀티텍스쳐링을 하는 방법과 텍스쳐 배열을 사용하는지에 대해 설명할 것입니다. 멀티텍스쳐링은 다른 두 텍스쳐를 섞어서 최종 텍스쳐를 얻는 과정입니다. 여러분이 텍스쳐를 혼합하는데 사용하는 방정식은 여러분이 성취하고자 하는바에 따라 다를 수 있습니다. 이번 강좌에서는 두 텍스쳐 색상의 평균값을 결합하여 균등하게 혼합된 최종 텍스쳐를 얻는 것을 볼 것입니다.
텍스쳐 배열은 DirectX 10부터 적용된 것으로 여러분이 gpu에서 한번에 여러 텍스쳐를 활성화를 가능하게 해줍니다. 이전 gpu에서 오직 단일 텍스쳐만 활성화되었을때 방법들은 항상 텍스쳐를 로드하고 언로드하는데 많은 추가 과정들이 초래했습니다. 대부분의 사람들은 이러한 문제를 텍스쳐 별칭을 사용하고 (하나의 큰 텍스쳐로 텍스쳐의 묶음을 로딩) UV 좌표를 다르게 사용하여 해결하였습니다. 그러나 이 새로운 특성으로 인해 텍스쳐 별칭 더이상 필요치 않습니다.
첫번째로 이 강좌에서 사용된 텍스쳐이며 우리는 기본 텍스쳐로 부를 것이고 다음과 같습니다.
두번째 텍스쳐는 첫번째것과 결합하는데 사용될 것이고 컬러 텍스쳐라 불릴 겁니다. 다음과 같습니다.
이 두 텍스쳐는 픽셀 쉐이더에서 픽셀대 픽셀로 결합될 것입니다. 우리가 사용할 블렌딩 방정식은 다음과 같습니다.
blendColor = basePixel * colorPixel * gammaCorrection;
저 방정식과 위 두 텍스쳐를 사용하여 다음과 같은 결과를 얻을 것입니다.
여러분들은 아마도 의아해 하실 겁니다. 왜 그냥 두 텍스쳐 픽셀의 평균값을 다음처럼 더하지 않는지..
blendColor = (basePixel * 0.5) + (colorPixel * 0.5);
그 이유는 우리에게 표현되는 픽셀 색상은 모니터의 감마값에 달려있기 때문입니다. 이는 픽셀 값을 0.0에서 1.0으로 곡선을 따르게 합니다. 따라서 비선형 컬러 값을 다루기 위해 픽셀 쉐이더에서 감마 보정이 필요합니다. 감마 보정을 하지 않고 단순히 평균을 더하면 다음과 같이 뭔가 젖은 듯한 결과를 얻게 됩니다.
또 중요한게 대부분의 장치들은 감마값이 달라 룩업테이블(미리 계산된 값이 나열된 테이블)이나 감마 슬라이더(보통의 모니터 화면 조정에 있는 것)를 통해 사용자가 장치에 대한 감마 설정을 할 수 있게 해줍니다. 이번 예제에서는 강좌를 쉽게 하기 위해 감마값을 2.0으로 하겠습니다.
먼저 텍스쳐 쉐이더 파일을 조금 변형한 멀티텍스쳐 쉐이더를 살펴봄으로 코드 섹션을 시작하겠습니다.
Multitexture.vs
버텍스 쉐이더는 이름만 바뀌었습니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: multitexture.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 MultiTextureVertexShader(VertexInputType input)
{
PixelInputType output;
// 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;
return output;
}
Multitexture.ps
////////////////////////////////////////////////////////////////////////////////
// Filename: multitexture.ps
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
서로 혼합될 두 텍스쳐에 대한 텍스쳐 배열 리소스를 추가하였습니다. 텍스쳐 배열은 그래픽 카드상 성능면에서 단일 텍스쳐 리소스를 사용하는 것보다 더 효율적입니다. 텍스쳐 스위칭은 이전 DirectX 버전에서 비용이 큰 작업이어서 대부분의 엔진들을 텍스쳐와 재질 스위치 중심으로 작성되어야 했습니다. 텍스쳐 배열은 그런 수행 비용을 줄이는데 도움이 됩니다.
Texture2D shaderTextures[2];
SamplerState SampleType;
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
픽셀 쉐이더가 이번 강좌의 모든 작업이 수행되는 곳입니다. 현재 두 텍스쳐의 좌표에서 픽셀의 샘플을 취합니다. 그 후에 픽셀 컬러를 곱하여 결합하는데 감마 보정때문에 비선형이기 때문입니다. 또 감마값을 곱하는데 대부분의 모니터의 감마값에 가까운 2.0를 사용하였습니다. 혼합된 픽셀을 얻은 뒤 0~1사이로 만들고(saturate) 최종 결과로 반환합니다. 또 텍스쳐 배열의 두 텍스쳐에 접근하는데 인덱싱을 사용한 것을 기억하시기 바랍니다.
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 MultiTexturePixelShader(PixelInputType input) : SV_TARGET
{
float4 color1;
float4 color2;
float4 blendColor;
// Get the pixel color from the first texture.
color1 = shaderTextures[0].Sample(SampleType, input.tex);
// Get the pixel color from the second texture.
color2 = shaderTextures[1].Sample(SampleType, input.tex);
// Blend the two pixels together and multiply by the gamma value.
blendColor = color1 * color2 * 2.0;
// Saturate the final color.
blendColor = saturate(blendColor);
return blendColor;
}
Multitextureshaderclass.h
multitexture shader 코드는 TextureShaderClass에서 약간 수정하였습니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: multitextureshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _MULTITEXTURESHADERCLASS_H_
#define _MULTITEXTURESHADERCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <d3dx11async.h>
#include <fstream>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// Class name: MultiTextureShaderClass
////////////////////////////////////////////////////////////////////////////////
class MultiTextureShaderClass
{
private:
struct MatrixBufferType
{
D3DXMATRIX world;
D3DXMATRIX view;
D3DXMATRIX projection;
};
public:
MultiTextureShaderClass();
MultiTextureShaderClass(const MultiTextureShaderClass&);
~MultiTextureShaderClass();
bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView**);
private:
bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*);
void ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView**);
void RenderShader(ID3D11DeviceContext*, int);
private:
ID3D11VertexShader* m_vertexShader;
ID3D11PixelShader* m_pixelShader;
ID3D11InputLayout* m_layout;
ID3D11Buffer* m_matrixBuffer;
ID3D11SamplerState* m_sampleState;
};
#endif
Multitextureshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: multitextureshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "multitextureshaderclass.h"
MultiTextureShaderClass::MultiTextureShaderClass()
{
m_vertexShader = 0;
m_pixelShader = 0;
m_layout = 0;
m_matrixBuffer = 0;
m_sampleState = 0;
}
MultiTextureShaderClass::MultiTextureShaderClass(const MultiTextureShaderClass& other)
{
}
MultiTextureShaderClass::~MultiTextureShaderClass()
{
}
bool MultiTextureShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
bool result;
multitexture HLSL 쉐이더 파일은 여기서 로드됩니다.
// Initialize the vertex and pixel shaders.
result = InitializeShader(device, hwnd, L"../Engine/multitexture.vs", L"../Engine/multitexture.ps");
if(!result)
{
return false;
}
return true;
}
Shutdown은 ShutdownShader 함수를 호출하여 쉐이더 관련 인터페이스들을 해제합니다.
void MultiTextureShaderClass::Shutdown()
{
// Shutdown the vertex and pixel shaders as well as the related objects.
ShutdownShader();
return;
}
이제 Render 함수는 텍스쳐 배열의 포인터를 받습니다. 이는 쉐이더에 혼할 계산을 위한 두 텍스쳐의 접근을 하게 해줄 것입니다.
bool MultiTextureShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix,
D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView** textureArray)
{
bool result;
// Set the shader parameters that it will use for rendering.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, textureArray);
if(!result)
{
return false;
}
// Now render the prepared buffers with the shader.
RenderShader(deviceContext, indexCount);
return true;
}
InitializeShader 함수는 정점 쉐이더와 픽셀 쉐이더 뿐만아니라 레이아웃, 매트릭스 버퍼와 샘플 state도 로드한다.
bool MultiTextureShaderClass::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;
// 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, "MultiTextureVertexShader", "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, "MultiTexturePixelShader", "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 matrix 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;
}
return true;
}
ShutdownShader는 InitializeShader 함수에서 셋업된 모든 인터페이스들을 해제합니다.
void MultiTextureShaderClass::ShutdownShader()
{
// 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;
}
OutputShaderErrorMessage 함수는 정점 쉐이더나 픽셀 쉐이더의 HLSL 파일을 컴파일중 문제가 있다면 에러를 파일에 기록합니다.
void MultiTextureShaderClass::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 MultiTextureShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix,
D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix,
ID3D11ShaderResourceView** textureArray)
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;
// 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 constant buffer.
dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the 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);
여기가 렌더링전에 텍스쳐 배열이 설정되는 곳입니다. PSSetShaderResources 함수는 텍스쳐 배열을 설정하는데 사용됩니다. 첫번째 파라미터는 배열에서 시작하는 곳(인덱스)입니다. 두번째 파라미터는 넘겨지는 배열에 든 텍스쳐의 개수 입니다. 그리고 세번째 파라미터는 텍스쳐 배열에 대한 포인터입니다.
// Set shader texture array resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 2, textureArray);
return true;
}
RenderShader 함수는 레이아웃, 쉐이더, 샘플러를 설정합니다. 그리고는 쉐이더를 이용하여 모델을 그립니다.
void MultiTextureShaderClass::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 triangles.
deviceContext->DrawIndexed(indexCount, 0, 0);
return;
}
Texturearrayclass.h
TextureArrayClass는 전에 사용된 TextureClass를 대체합니다. 이제 단순 단일 텍스쳐를 가지지 않고 다수의 텍스쳐를 가질 수 있고 필요로 하는 오브젝트들에게 접근할 수 있게 됩니다. 이번 강좌는 그저 텍스쳐 두개만 다루지만 쉽게 확장될 수 있습니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: texturearrayclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTUREARRAYCLASS_H_
#define _TEXTUREARRAYCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx11tex.h>
////////////////////////////////////////////////////////////////////////////////
// Class name: TextureArrayClass
////////////////////////////////////////////////////////////////////////////////
class TextureArrayClass
{
public:
TextureArrayClass();
TextureArrayClass(const TextureArrayClass&);
~TextureArrayClass();
bool Initialize(ID3D11Device*, WCHAR*, WCHAR*);
void Shutdown();
ID3D11ShaderResourceView** GetTextureArray();
private:
여기 원소 2개짜리 텍스쳐 배열이 This is the two element texture array private variable.
ID3D11ShaderResourceView* m_textures[2];
};
#endif
Texturearrayclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: texturearrayclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "texturearrayclass.h"
클래스 생성자는 텍스쳐 배열 원소들을 null로 초기화합니다.
TextureArrayClass::TextureArrayClass()
{
m_textures[0] = 0;
m_textures[1] = 0;
}
TextureArrayClass::TextureArrayClass(const TextureArrayClass& other)
{
}
TextureArrayClass::~TextureArrayClass()
{
}
Initialize 함수는 두 텍스쳐 파일 이름을 받아서 해당 파일로부터 텍스쳐 배열안에 두 텍스쳐 자원을 생성합니다.
bool TextureArrayClass::Initialize(ID3D11Device* device, WCHAR* filename1, WCHAR* filename2)
{
HRESULT result;
// Load the first texture in.
result = D3DX11CreateShaderResourceViewFromFile(device, filename1, NULL, NULL, &m_textures[0], NULL);
if(FAILED(result))
{
return false;
}
// Load the second texture in.
result = D3DX11CreateShaderResourceViewFromFile(device, filename2, NULL, NULL, &m_textures[1], NULL);
if(FAILED(result))
{
return false;
}
return true;
}
Shutdown 함수는 텍스쳐 배열의 각 원소를 해제합니다.
void TextureArrayClass::Shutdown()
{
// Release the texture resources.
if(m_textures[0])
{
m_textures[0]->Release();
m_textures[0] = 0;
}
if(m_textures[1])
{
m_textures[1]->Release();
m_textures[1] = 0;
}
return;
}
GetTextureArray 함수는 텍스쳐 배열의 포인터를 반환하여 함수를 호출하는 객체는 텍스쳐 배열내 텍스쳐들에 접근할 수 있습니다.
ID3D11ShaderResourceView** TextureArrayClass::GetTextureArray()
{
return m_textures;
}
Modelclass.h
ModelClass는 텍스쳐 배열을 사용하도록 수정되었습니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <fstream>
using namespace std;
TextureArrayClass 헤더파일이 이전의 TextureClass 헤더를 대신하여 포함되었습니다.
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "texturearrayclass.h"
////////////////////////////////////////////////////////////////////////////////
// Class name: ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR2 texture;
};
struct ModelType
{
float x, y, z;
float tu, tv;
float nx, ny, nz;
};
public:
ModelClass();
ModelClass(const ModelClass&);
~ModelClass();
bool Initialize(ID3D11Device*, char*, WCHAR*, WCHAR*);
void Shutdown();
void Render(ID3D11DeviceContext*);
int GetIndexCount();
ID3D11ShaderResourceView** GetTextureArray();
private:
bool InitializeBuffers(ID3D11Device*);
void ShutdownBuffers();
void RenderBuffers(ID3D11DeviceContext*);
bool LoadTextures(ID3D11Device*, WCHAR*, WCHAR*);
void ReleaseTextures();
bool LoadModel(char*);
void ReleaseModel();
private:
ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
int m_vertexCount, m_indexCount;
ModelType* m_model;
이제 TextureClass 변수 대신 TextureArrayClass 변수를 가집니다.
TextureArrayClass* m_TextureArray;
};
#endif
Modelclass.cpp
이전 강좌이후 바뀐 함수들만 다룰 것입니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "modelclass.h"
ModelClass::ModelClass()
{
m_vertexBuffer = 0;
m_indexBuffer = 0;
m_model = 0;
생성자에서 새 TextureArray변수를 초기화합니다.
m_TextureArray = 0;
}
bool ModelClass::Initialize(ID3D11Device* device, char* modelFilename, WCHAR* textureFilename1, WCHAR* textureFilename2)
{
bool result;
// Load in the model data,
result = LoadModel(modelFilename);
if(!result)
{
return false;
}
// Initialize the vertex and index buffers.
result = InitializeBuffers(device);
if(!result)
{
return false;
}
이번 모델에서 텍스쳐배열에 로드되고 혼합된 결과로 렌더링 될 텍스쳐에 대한 여러 파일 이름을 받는 LoadTextures 함수를 호출합니다.
// Load the textures for this model.
result = LoadTextures(device, textureFilename1, textureFilename2);
if(!result)
{
return false;
}
return true;
}
void ModelClass::Shutdown()
{
Shutdown 함수에서 ReleaseTextures 함수를 호출하여 텍스쳐 배열을 해제합니다.
// Release the model textures.
ReleaseTextures();
// Shutdown the vertex and index buffers.
ShutdownBuffers();
// Release the model data.
ReleaseModel();
return;
}
호출하는 객체에게 모델 렌더링에 사용될 텍스쳐 배열로의 접근을 가능케 해주는 GetTextureArray 함수를 새로 가집니다.
ID3D11ShaderResourceView** ModelClass::GetTextureArray()
{
return m_TextureArray->GetTextureArray();
}
LoadTextures 함수는 함수에 매개변수로 들어온 두 텍스쳐를 로딩을 하기위해 TextureArrayClass 객체를 생성하고 초기화하도록 바뀌었습니다.
bool ModelClass::LoadTextures(ID3D11Device* device, WCHAR* filename1, WCHAR* filename2)
{
bool result;
// Create the texture array object.
m_TextureArray = new TextureArrayClass;
if(!m_TextureArray)
{
return false;
}
// Initialize the texture array object.
result = m_TextureArray->Initialize(device, filename1, filename2);
if(!result)
{
return false;
}
return true;
}
ReleaseTextures 함수는 TextureArrayClass 객체를 해제합니다.
void ModelClass::ReleaseTextures()
{
// Release the texture array object.
if(m_TextureArray)
{
m_TextureArray->Shutdown();
delete m_TextureArray;
m_TextureArray = 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"
GraphicsClass에서 MultiTextureShaderClass에 대한 헤더를 포함합니다.
#include "multitextureshaderclass.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;
여기서 새로운 MultiTextureShaderClass 객체를 생성하였습니다.
MultiTextureShaderClass* m_MultiTextureShader;
};
#endif
Graphicsclass.cpp
이전 강좌에서 바뀐 함수만을 다룰 것입니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"
GraphicsClass::GraphicsClass()
{
m_D3D = 0;
m_Camera = 0;
m_Model = 0;
생성자에서 새 mulltitexture shader 객체를 null로 초기화합니다.
m_MultiTextureShader = 0;
}
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
bool result;
D3DXMATRIX baseViewMatrix;
// 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;
}
// Initialize a base view matrix with the camera for 2D user interface rendering.
m_Camera->SetPosition(0.0f, 0.0f, -1.0f);
m_Camera->Render();
m_Camera->GetViewMatrix(baseViewMatrix);
// Create the model object.
m_Model = new ModelClass;
if(!m_Model)
{
return false;
}
이제 ModelClass 객체는 좀 다르게 초기화됩니다. 이번 강좌에는 간단히 사각형 평면 위에서 잘 보여지는지 확인만을 목적으로 square.txt 모델을 로드합니다. 또 이전 강좌에서처럼 단일 텍스쳐가 아닌 텍스쳐 배열을 위한 두 텍스쳐를 로드합니다.
// Initialize the model object.
result = m_Model->Initialize(m_D3D->GetDevice(), "../Engine/data/square.txt", L"../Engine/data/stone01.dds",
L"../Engine/data/dirt01.dds");
if(!result)
{
MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
return false;
}
여기 새 multitexture shader 객체를 생성하고 초기화합니다.
// Create the multitexture shader object.
m_MultiTextureShader = new MultiTextureShaderClass;
if(!m_MultiTextureShader)
{
return false;
}
// Initialize the multitexture shader object.
result = m_MultiTextureShader->Initialize(m_D3D->GetDevice(), hwnd);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the multitexture shader object.", L"Error", MB_OK);
return false;
}
return true;
}
void GraphicsClass::Shutdown()
{
Shutdown 함수에서 multitexture shader를 해제합니다.
// Release the multitexture shader object.
if(m_MultiTextureShader)
{
m_MultiTextureShader->Shutdown();
delete m_MultiTextureShader;
m_MultiTextureShader = 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()
{
블렌딩 효과를 좀더 쉽게 보기 위해 카메라의 위치를 약간 더 가깝게 설정하였습니다.
// Set the position of the camera.
m_Camera->SetPosition(0.0f, 0.0f, -5.0f);
return true;
}
bool GraphicsClass::Render()
{
D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix, orthoMatrix;
// 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, projection, and ortho matrices from the camera and D3D objects.
m_D3D->GetWorldMatrix(worldMatrix);
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
m_D3D->GetOrthoMatrix(orthoMatrix);
// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
m_Model->Render(m_D3D->GetDeviceContext());
multitexture shader를 모델 렌더링에 사용합니다. 주의할 점은 텍스쳐 배열을 ModelClass에서 입력으로 쉐이더에 보내야 합니다.
// Render the model using the multitexture shader.
m_MultiTextureShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
m_Model->GetTextureArray());
// Present the rendered scene to the screen.
m_D3D->EndScene();
return true;
}
Summary
이제 두 텍스쳐를 고르게 결합하고 감마 보정을 적용시킬 쉐이더를 갖게 되었습니다. 또 향상된 그래픽스 퍼포먼스를 위해 텍스쳐 배열을 어떻게 사용하는지에 대해서도 배웠습니다.
연습하기
1. 코드를 재컴파일하고 프로그램을 실행하여 결과 이미지 확인하기. 종료하기 위해 esc버튼을 누르세요.
2. 다른 텍스쳐들로 바꿔서 결과 보기.
'프로그래밍 > directx' 카테고리의 다른 글
DirectX 11 Tutorials - 16 : 절두체(화면영역) 고르기 (0) | 2016.06.26 |
---|---|
DirectX 11 Tutorials - 15 : FPS, CPU 사용량 그리고 타이머 (1) | 2016.06.23 |
DirectX 11 Tutorials - 14 : Direct 사운드 (0) | 2016.06.20 |
DirectX 11 Tutorials - 13 : Direct 입력 (2) | 2016.06.18 |
DirectX 11 Tutorials - 12 : 폰트 엔진 (0) | 2016.06.15 |