728x90

원문 : http://www.rastertek.com/dx11tut05.htmld


Tutorial 5: Texturing


이번 강좌는 DirectX 11에서 텍스쳐링을 어떻게 사용하지 설명할 것입니다. 텍스쳐링은 사진이나 이미지를 폴리곤의 면에 적용하여 씬에 실사적으로 묘사해 줍니다. 예를들어 이번 강좌에서는 다음 이미지를 사용할 것입니다.



그리고 저번 강좌의 폴리곤에 다음과 같이 적용할 것입니다.



우리가 사용할 텍스쳐의 포맷은 .dds 파일입니다. 이는 Direct Draw Surface 포맷으로 DirectX에서 사용합니다. .dds 파일을 생성하는데 사용하는 툴은 DIrectX SDK에 포함되어 있습니다. DirectX Utilities에 속하고 DirectX Texture Tool 이라고 부릅니다. 여러분은 어떤 사이즈나 포맷이든 텍스쳐를 만들 수 있으며 다른 텍스쳐를 잘라서 붙이고 .dds 파일로 저장할 수도 있습니다. 사용하기 매우 간단합니다.


코드를 살펴보기 전에 먼저 텍스쳐 맵핑이 어떻게 동작하는지에 대해 논의하여야 합니다. .dds 파일 이미지에서 폴리곤으로 픽셀을 맵핑하기 위해 '텍셀(texel = texture + pixel) 좌표계'라고 부르는 것을 사용합니다. 이 좌표계는 픽셀의 정수값을 0.0f ~ 1.0f사이의 실수값으로 변환합니다. 예를들어 텍스쳐의 너비가 256픽셀 일때 첫번째 픽셀은 0.0f 으로 맵핑되고 256번째 픽셀은 1.0f로 맵핑되고 128번째인 중간 픽셀은 0.5f로 맵핑됩니다.


텍셀 좌표계에서 너비값을 "U", 높이값을 "V"라고 부릅니다. 너비는 왼쪽부터 0.0, 오른쪽 끝이 1.0이고 높이는 위쪽부터 0.0, 맨 아래가 1.0입니다. 예를 들어 좌상단은 U 0.0, V 0.0 을 우하단은 U 1.0, V 1.0 을 나타냅니다. 아래 텍셀 좌표계를 설명하기 위해 그림을 만들었습니다.



이제 텍스쳐를 폴리곤에 어떻게 맵핑하는지 기본은 알았음으로 이번 강좌에 새로 업데이트된 프레임워크를 보겠습니다.



이전 강좌 이후 프레임워크의 새로운 변화는 ModelClass안에 TextureClass가 추가된 것과 새로운 TextureShaderClass가 ColorShaderClass를 대체한 것입니다. 먼저 HLSL 텍스쳐 쉐이더를 보는 것으로 코드탐방을 시작합니다.




Texture.vs


텍스쳐 버텍스 쉐이더는 텍스쳐링을 위한 부분을 제외하고 이전 color 쉐이더와 매우 비슷합니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: texture.vs
////////////////////////////////////////////////////////////////////////////////


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


더이상 버텍스 타입에서 색상을 사용하지 않고 대신에 텍스쳐 좌표를 사용합니다. 텍스쳐 좌표는 실수인 U, V 두개를 가지기 때문에 float2형을 사용합니다. 버텍스, 픽셀 쉐이더를 위한 텍스쳐 좌표의 의미(예약어?)는 TEXCOORD0입니다. 0부터 어떤숫자든 필요한 텍스쳐 좌표만큼 만들 수 있습니다.

////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; }; struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; }; //////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType TextureVertexShader(VertexInputType input) { PixelInputType output; // 행렬연산을 위해 위치벡터 4번째 요소에 1을 대입합니다. input.position.w = 1.0f; // 월드, 뷰, 투영 행렬에 대해 정점의 좌표를 계산합니다. output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix);

이번 강좌의 color 버텍스 쉐이더와 비교하여 텍스쳐 버텍스 쉐이더의 다른점은 입력 버텍스로부터 색상의 복사를 취하는 대신 텍스쳐 좌표를 복사하여 픽셀 쉐이더로 넘깁니다.

// 픽셀 쉐이더를 위해 텍스쳐 좌표를 저장합니다. output.tex = input.tex; return output; }



Texture.ps


////////////////////////////////////////////////////////////////////////////////
// Filename: texture.ps
////////////////////////////////////////////////////////////////////////////////


텍스쳐 픽셀 쉐이더는 2개의 전역 변수를 가집닏. 하나는 텍스쳐 리소스인 Texture2D shaderTexture 입니다. 이것은 모델에 텍스쳐를 렌더링할때 사용될 텍스쳐 리소스가 될 것입니다. 두번째는 SamplerState SampleType 입니다. sampler state는 픽셀이 폴리곤의 면에 어떻게 쓰여질지를 바꾸도록 해줍니다. 예를 들어 폴리곤이 엄청 멀리 있어서 화면상에 8픽셀로만 구성될 때 sample state을 사용하여 원래 텍스쳐의 어떤 픽셀 혹은 픽셀 결합이 실제로 그려질지를 알아냅니다. 본래 텍스쳐가 256x256 이라면 어떤 픽셀이 실제로 그려질지를 결정하는 것은 해당 텍스쳐가 멀어서 작게보일때 여전히 같은 물체로 보여야 하므로 매우 중요합니다. TextureShaderClass에 sampler state를 설정하고 리소스 포인터에 붙여 픽셀셀 쉐이더가 그릴 픽셀의 샘플을 결정하는데 사용하도록 할 것입니다.


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


텍스처 픽셀 쉐이더의 PixelInputType은 색상값 대신에 텍스쳐 좌표를 사용하는 것으로 바뀌었습니다.


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


픽셀 쉐이더는 HLSL sample 함수를 사용하는 것으로 바뀌었습니다. sample 함수는 우리가 위에서 정의한 sample state와 픽셀을 위한 텍스쳐 좌표를 사용합니다. 이 두 변수를 폴리곤 면의 UV 위치에 대한 픽셀값을 결정하고 반환하는데 사용합니다.

//////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 TexturePixelShader(PixelInputType input) : SV_TARGET { float4 textureColor; // 텍스쳐 좌표 위치에서 sampler를 사용하여 텍스쳐로부터 픽셀 색상을 적용합니다. textureColor = shaderTexture.Sample(SampleType, input.tex); return textureColor; }



Textureclass.h


TextureClass는 텍스처 리소스에 대한 로딩, 언로딩, 접근을 캡슐화 합니다. 각 텍스쳐는 반드시 이 실체화된 클래스 객체를 필요로 합니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: textureclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTURECLASS_H_
#define _TEXTURECLASS_H_


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


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


첫 2개의 함수는 넘겨 받은 파일이름의 파일로부터 텍스쳐를 로딩하고 더이상 필요 없을때 언로딩합니다.


	bool Initialize(ID3D11Device*, WCHAR*);
	void Shutdown();


GetTexture() 함수는 쉐이더가 렌더링하는데 사용할 수 있도록 텍스쳐 리소스에 대한 포인터를 반환합니다.


	ID3D11ShaderResourceView* GetTexture();

private:


이것은 private속성의 텍스쳐 리소스입니다.


	ID3D11ShaderResourceView* m_texture;
};

#endif




Textureclass.cpp


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


클래스 생성자는 텍스쳐 리소스 포인터를 null로 초기화합니다.


TextureClass::TextureClass()
{
	m_texture = 0;
}


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


TextureClass::~TextureClass()
{
}


Initialize() 함수는 Direct3D 디바이스아 텍스쳐 파일이름을 가지고 m_texture라 부르는 쉐이더 리소스 변수에 텍스쳐 파일을 로딩합니다. 그럼 텍스쳐는 렌더링하는데 사용될 수 있습니다.

bool TextureClass::Initialize(ID3D11Device* device, WCHAR* filename) { HRESULT result; // 텍스쳐 로딩 result = D3DX11CreateShaderResourceViewFromFile(device, filename, NULL, NULL, &m_texture, NULL); if(FAILED(result)) { return false; } return true; }

Shutdown() 함수는 텍스쳐 리소스가 로딩되어있으면 해제하고 포인터를 null로 설정합니다.

void TextureClass::Shutdown() { // 텍스처 리소스 해제. if(m_texture) { m_texture->Release(); m_texture = 0; } return; }

GetTexture() 다른 객체에서 텍스쳐를 렌더링에 사용하기 위해 텍스쳐 쉐이더 리소스로 접근할때 사용하는 함수입니다.


ID3D11ShaderResourceView* TextureClass::GetTexture()
{
	return m_texture;
}




Modelclass.h


ModelClass는 이전 강좌 이후 텍스쳐링을 수용하기 위해 바뀌었습니다.


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


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


TextureClass 헤더는 ModelClass헤더에 인클루드 되었습니다.


///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "textureclass.h"


////////////////////////////////////////////////////////////////////////////////
// Class name: ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:


VertexType은 색상 요소를 텍스쳐 좌표 요소로 대체되었습니다. 텍스쳐 좌표는 이전 강좌에서 쓰인 초록색을 대신합니다.


	struct VertexType
	{
		D3DXVECTOR3 position;
		D3DXVECTOR2 texture;
	};

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

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

	int GetIndexCount();


ModelClass도 GetTexture() 함수를 가짐으로 텍스쳐 리소스를 모델을 그릴 쉐이더로 보낼 수 있습니다.


	ID3D11ShaderResourceView* GetTexture();

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


ModelClass는 모델을 렌더링하는데 쓰일 텍스쳐를 로딩하고 언로딩하는 private속성의 LoadTexture()와 ReleaseTexture()를 가집니다.


	bool LoadTexture(ID3D11Device*, WCHAR*);
	void ReleaseTexture();

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


m_Texture 변수는 모델을 위한 텍스쳐 리소스를 로딩하고 해제하고 접근하는데 사용됩니다.


	TextureClass* m_Texture;
};

#endif




Modelclass.cpp


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


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


생성자는 새 텍스쳐 객체를 null로 초기화합니다.


	m_Texture = 0;
}


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


ModelClass::~ModelClass()
{
}


Initialize() 함수는 입력으로 모델이 사용할 .dds 텍스쳐 파일 이름도 필요합니다.


bool ModelClass::Initialize(ID3D11Device* device, WCHAR* textureFilename) { bool result; // 삼각형의 기하정보를 가지고 있는 버텍스, 인덱스 버퍼를 초기화합니다. result = InitializeBuffers(device); if(!result) { return false; }


이제 Initialize() 함수는 텍스쳐를 로딩할 새 private 함수를 호출합니다.

// 현재 모델을 위한 텍스쳐를 로딩합니다. result = LoadTexture(device, textureFilename); if(!result) { return false; } return true; } void ModelClass::Shutdown() {

이제 Shutdown() 함수는 초기화에서 로딩했던 텍스쳐 객체를 해제하기 위해 새 private 함수를 호출합니다.

// 모델 텍스쳐를 해제합니다. ReleaseTexture(); // 버텍스, 인덱스 버퍼를 해제합니다. ShutdownBuffers(); return; } void ModelClass::Render(ID3D11DeviceContext* deviceContext) { // 그리기 위해 버텍스, 인덱스 버퍼를 준비하도록 그래픽 파이프라인에 넣습니다. RenderBuffers(deviceContext); return; } int ModelClass::GetIndexCount() { return m_indexCount; }

GetTexture() 함수는 모델 텍스쳐 리소스를 반환합니다. 텍스쳐 쉐이더는 모델을 렌더링하기 위해 이 텍스쳐에 접근해야 합니다.

ID3D11ShaderResourceView* ModelClass::GetTexture() { return m_Texture->GetTexture(); } bool ModelClass::InitializeBuffers(ID3D11Device* device) { VertexType* vertices; unsigned long* indices; D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; D3D11_SUBRESOURCE_DATA vertexData, indexData; HRESULT result; // 버텍스 배열에 정점 개수 설정. m_vertexCount = 3; // 인덱스 배열에 인덱스 개수 설정. m_indexCount = 3; // 버텍스 배열 생성. vertices = new VertexType[m_vertexCount]; if(!vertices) { return false; } // 인덱스 배열 설정. indices = new unsigned long[m_indexCount]; if(!indices) { return false; }

버텍스 배열은 이제 색상 요소 대신 텍스쳐 요소를 가집니다. 텍스쳐 벡터는 항상 첫번쨰는 U이고 2번째는 V입니다. 예를 들어 텍스쳐 좌표가 삼각형의 왼쪽 아래이면 U 0.0, V 1.0에 상응합니다. 헷갈리시는 분들은 이 강좌 맨 위에 UV좌표 그림을 보시면 이해가 가실 겁니다.(U는 가장 왼쪽이 0.0, V는 가장 위쪽이 0.0) 텍스쳐의 어느 부분에서 폴리곤 면의 어느 부분으로 맵핑하기 위해 좌표계를 바꿀 수 있어야 하는 것을 기억하시기 바랍니다. 이번 강좌에서는 맵핑을 간단하게 직접 넣었습니다.


	// Load the vertex array with data.
	vertices[0].position = D3DXVECTOR3(-1.0f, -1.0f, 0.0f);  // Bottom left.
	vertices[0].texture = D3DXVECTOR2(0.0f, 1.0f);

	vertices[1].position = D3DXVECTOR3(0.0f, 1.0f, 0.0f);  // Top middle.
	vertices[1].texture = D3DXVECTOR2(0.5f, 0.0f);

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

	// Load the index array with data.
	indices[0] = 0;  // Bottom left.
	indices[1] = 1;  // Top middle.
	indices[2] = 2;  // Bottom right.

	// Set up the description of the vertex buffer.
	vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = 0;
	vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	// 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;

	// 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 ModelClass::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;
}


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


LoadTexture() 함수는 텍스쳐 객체를 생성하고 입력으로 들어온 파일 이름과 디바이스로 초기화하는 새 private 함수입니다. 이 함수는 초기화시 호출됩니다.

bool ModelClass::LoadTexture(ID3D11Device* device, WCHAR* filename) { bool result; // 텍스쳐 객체 생성. m_Texture = new TextureClass; if(!m_Texture) { return false; } // 텍스쳐 객체 초기화. result = m_Texture->Initialize(device, filename); if(!result) { return false; } return true; }

ReleaseTexture() 함수는 LoadTexture() 함수에서 생성되고 로딩된 텍스쳐 객체를 해제할 것입니다.

void ModelClass::ReleaseTexture() { // 텍스쳐 객체 해제. if(m_Texture) { m_Texture->Shutdown(); delete m_Texture; m_Texture = 0; } return; }



Textureshaderclass.h


TextureShaderClass는 이전 강좌의 ColorShaderClass의 업데이트된 버전입니다. 이 클래스는 버텍스, 픽셀 쉐이더를 이용하여 3D 모델을 그리는데 사용될 것입니다.


////////////////////////////////////////////////////////////////////////////////
// Filename: textureshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTURESHADERCLASS_H_
#define _TEXTURESHADERCLASS_H_


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


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

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

	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;


sampler state에 대한 새 private 포인터 변수가 있습니다. 이 포인터는 텍스쳐 쉐이더에 대한 인터페이스로 사용될 것입니다.


	ID3D11SamplerState* m_sampleState;
};

#endif



Textureshaderclass.cpp


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


TextureShaderClass::TextureShaderClass()
{
	m_vertexShader = 0;
	m_pixelShader = 0;
	m_layout = 0;
	m_matrixBuffer = 0;


새 sampler 변수는 생성자에서 null로 설정됩니다.


	m_sampleState = 0;
}


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


TextureShaderClass::~TextureShaderClass()
{
}


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


새 texture.vs 와 texture.ps HLSL 파일은 이 쉐이더를 위해 로딩됩니다.

// 버텍스, 픽셀 쉐이더를 초기화합니다. result = InitializeShader(device, hwnd, L"../Engine/texture.vs", L"../Engine/texture.ps"); if(!result) { return false; } return true; }

Shutdown() 함수는 쉐이더를 해제하는 함수를 호출합니다.

void TextureShaderClass::Shutdown() { // 버텍스, 픽셀 쉐이더와 관련 객체들을 정리합니다. ShutdownShader(); return; }

Render() 함수는 텍스처 리소스에 대한 포인터인 texture라 부르는 파라미터가 늘었습니다. SetShaderParameters() 함수로 보내져서 쉐이더에서 설정되어 렌더링에 사용됩니다.

bool TextureShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount,

D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,

D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture)

{ bool result; // 렌더링에 사용할 쉐이더 파라미터를 설정합니다. result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture); if(!result) { return false; } // 준비된 버퍼를 쉐이더로 렌더링합니다. RenderShader(deviceContext, indexCount); return true; }

InitializeShader() 함수는 텍스쳐 쉐이더를 설정합니다.


bool TextureShaderClass::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;


이 함수에서 설정될 텍스쳐 sampler의 description을 가지는 변수가 새로 생겼습니다.

D3D11_SAMPLER_DESC samplerDesc; // 이 함수에서 사용할 포인터를 null로 초기화합니다. errorMessage = 0; vertexShaderBuffer = 0; pixelShaderBuffer = 0;

새 텍스쳐를 버텍스, 픽셀 쉐이더에 로딩합니다.


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

"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 pixel shader from the buffer. result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(),

pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader); if(FAILED(result)) { return false; }


입력 레이아웃은 색상대신 텍스쳐 요소를 가짐으로 바뀌어졌습니다. 첫번째 포지션 부분은 바뀌지 않았지만 두번째 부분의 SemanticName과 Format은 TEXCOORD 과 DXGI_FORMAT_R32G32_FLOAT으로 바뀌었습니다. 이 두 변화는 ModelClass 정의와 쉐이더 파일에 있는 새 VertexType에 따라 조정되는 것입니다.


	// 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 matrix 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; }


sampler state description은 여기서 설정하고 나중에 쉐이더로 보냅니다. 텍스쳐 sampler description의 가장 중요한 요소는 바로 Filter 입니다. Filter는 폴리곤 면 위 텍스쳐의 최종 결과를 만들기 위해 사용되거나 결합되는 픽셀을 어떻게 결정할지를 판단합니다. 예를 들어 여기서 저는 D3D11_FILTER_MIN_MAG_MIP_LINEAR를 사용했는데 계산비용은 비싸지만 최고의 결과물을 보여줍니다. 이 필터는 sampler에게 확대, 축소, 밉맵핑에 선형보간법을 사용한다고 알려줍니다.


AddressU 와 AddressV는 좌표를 0.0f ~ 1.0f로 고정시킵니다. 그 이상, 이하의 좌표들도 0.0f ~ 1.0f으로 맞춥니다. 다른 sampler state description 설정은 기본으로 합니다.


	// 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() 함수는 TextureShaderClass에서 사용된 모든 변수를 해제합니다. 이제 초기화에서 생성된 sampler state도 해제합니다.


void TextureShaderClass::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 TextureShaderClass::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 TextureShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix,

D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture) { 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 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 constant buffer. deviceContext->Unmap(m_matrixBuffer, 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_matrixBuffer);


SetShaderParameters() 함수는 픽셀 쉐이더의 텍스쳐 설정 포함하도록 수정되었습니다.


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

	return true;
}


RenderShader() 함수는 폴리곤을 렌더링하기위해 쉐이더를 호출합니다.


void TextureShaderClass::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);


RenderShader() 함수는 렌더링전에 픽셀 쉐이더에 sample state설정을 포함하도록 바뀌었습니다.


	// 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_


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


GraphicsClass는 ColorShaderClass가 지워지고 대신에 TextureShaderClass 헤더를 인클루드 합니다.


#include "textureshaderclass.h"


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


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

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

private:
	bool Render();

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


TextureShaderClass private 객체 변수가 추가되었습니다.


	TextureShaderClass* m_TextureShader;
};

#endif



Graphicsclass.cpp


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


m_TextureShader 변수는 생성자에서 null로 초기화 합니다.


GraphicsClass::GraphicsClass()
{
	m_D3D = 0;
	m_Camera = 0;
	m_Model = 0;
	m_TextureShader = 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;
	}


ModelClass::Initialize() 함수는 이제 텍스처의 모델을 렌더링하는데 사용될 텍스쳐의 이름을 받습니다.


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


새로운 TextureShaderClass 객체를 생성하고 초기화하는 코드도 추가되었습니다.


	// 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;
	}

	return true;
}


void GraphicsClass::Shutdown()
{


TextureShaderClass 객체는 Shutdown() 함수에서 역시 해제됩니다.


	// Release the texture shader object.
	if(m_TextureShader)
	{
		m_TextureShader->Shutdown();
		delete m_TextureShader;
		m_TextureShader = 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 Direct3D object.
	if(m_D3D)
	{
		m_D3D->Shutdown();
		delete m_D3D;
		m_D3D = 0;
	}

	return;
}


bool GraphicsClass::Frame()
{
	bool result;


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

	return true;
}


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


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

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

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

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


텍스쳐 쉐이더는 이제 모델을 렌더링하기 위해 컬러 쉐이더 대신에 호출됩니다. 또 쉐이더에서 텍스쳐에 접근하기 위해 모델 객체에서 텍스쳐 리소스 포인터를 받는것을 기억하시기 바랍니다.


	// Render the model using the texture shader.
	result = m_TextureShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(),

worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture()); if(!result) { return false; } // Present the rendered scene to the screen. m_D3D->EndScene(); return true; }




요약


여러분은 이제 텍스쳐를 로딩하고 폴리곤 면에 맵핑한 뒤 쉐이더로 렌더링을 하는 기본을 이해하셨을 것입니다.




연습하기


1. 재컴파일하고 텍스쳐가 맵핑된 삼각형이 잘 보이는지 확인하시기 바랍니다. 그리고 esc버튼으로 종료하시면 됩니다.


2. 여러분만의 .dds 텍스쳐를 생성하여 같은 디렉토리에 위치시키고 GraphicsClass::Initialize() 함수에 텍스쳐 이름을 변경하여 재컴파일후 확인해보세요.


3. 삼각형 두개로 사각형을 만들도록 코드를 바꾸세요. 이 사각형에 텍스쳐를 맵핑하여 잘 나오는지 확인해 보세요.


4. MIN_MAG_MIP_LINEAR 필터의 효과를 확인해 보기 위해 카메라 거리를 이동해 보세요.


5. 다른 필터를 적용해 보고 카메라 거리를 움직여 보고 다른 결과를 확인해 보세요.



소스 only


dx11src05.zip


728x90

+ Recent posts