ФЭНДОМ


ВведениеПравить

В этом уроке мы научимся использовать текстуру. О текстурах вы можете почитать здесь или более полно здесь (но на английском). Кратко, текстурирование - это проекция двухмерного изображения на трехмерную геометрию.
Текстура, как я сказал выше, это двухмерное изображение (на самом деле есть еще и одномерные и трехмерные текстуры, но мы их не рассмариваем), то есть имеет две оси u и v. Ось U расположена горизонтально изображения, а ось V - вертикально. При этом точка 0 является началом длины изображения, а точка 1 - концом длины изображения. Таким образом точка расположенная в середине изображения имеет (0.5;0.5) координату.

DirectXStepToStep Lesson7 1

При этом если мы заданим не 1, а к примеру 2 по любой из оси или сразу по обоим, то мы спроецируем не одну а уже две текстуры на каждой из осей.

Для урока нам нужна любая текстура (то есть любое изображение с кратным размером, к примеру 256 на 256 или 512 на 512). Вы можете взять вот эту (сохраните ее, и используйте) или свою собственную.

Texture

Пишем кодПравить

Создайте проект, подключите фреймворк третьей версии. Файл main.cpp

#include "MyRender.h"

int main()
{
	Framework framework;

	MyRender *render = new MyRender();

	FrameworkDesc desc;
	desc.render = render;

	framework.Init(desc);

	framework.Run();

	framework.Close();

	return 0;
}

Файл MyRender.h

#pragma once

#include "D3D11_Framework.h"
#include <xnamath.h>

using namespace D3D11Framework;

class MyRender : public Render
{
public:
	MyRender();
	bool Init(HWND hwnd);
	bool Draw();
	void Close();

	void* operator new(size_t i)
	{
		return _aligned_malloc(i,16);
	}

	void operator delete(void* p)
	{
		_aligned_free(p);
	}

private:		
	ID3D11Buffer *m_pVertexBuffer;
	ID3D11InputLayout *m_pVertexLayout;	
	ID3D11VertexShader *m_pVertexShader;
	ID3D11PixelShader *m_pPixelShader;
	ID3D11Buffer *m_pIndexBuffer;
	ID3D11Buffer *m_pConstantBuffer;

	ID3D11ShaderResourceView *m_pTextureRV;
	ID3D11SamplerState *m_pSamplerLinear;

	XMMATRIX m_World1;
	XMMATRIX m_World2;
	XMMATRIX m_View;
	XMMATRIX m_Projection;

	float m_rot;
};

Мы добавили два новых члена m_pTextureRV, который будет хранить текстуру в памяти и m_pSamplerLinear который описывает представление текстуры - фильтрацию, MIP и адрессацию. Текстура, как вы заметили, является шейдер-ресурсом.

MyRender.cpp:

#include "MyRender.h"

struct SimpleVertex
{
	XMFLOAT3 Pos;
	XMFLOAT2 Tex;
};

struct ConstantBuffer
{
	XMMATRIX  WVP;
};

MyRender::MyRender()
{
	m_pVertexShader = nullptr;
	m_pPixelShader = nullptr;
	m_pVertexLayout = nullptr;
	m_pVertexBuffer = nullptr;
	m_pIndexBuffer = nullptr;
	m_pConstantBuffer = nullptr;

	m_pTextureRV = nullptr;
	m_pSamplerLinear = nullptr;
	m_rot = 0.01;
}

bool MyRender::Init(HWND hwnd)
{
	HRESULT hr = S_OK;
	ID3DBlob* pVSBlob = NULL;
	hr = m_compileshaderfromfile(L"shader.fx", "VS", "vs_4_0", &pVSBlob);
	if( FAILED( hr ) )
	{
		Log::Get()->Err("Невозможно скомпилировать файл shader.fx. Пожалуйста, запустите данную программу из папки, содержащей этот файл");
		return false;
	}

	hr = m_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pVertexShader );
	if( FAILED( hr ) )
	{	
		_RELEASE(pVSBlob);
		return false;
	}

Здесь все как и раньше. Только константный буфер упрощен, в шейдер мы будем передавать сразу готовую матрицу (помните, я ранее писал что чем меньше действий в шейдере тем лучше?).

	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	UINT numElements = ARRAYSIZE( layout );

Описываем формат ввода.

	hr = m_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &m_pVertexLayout );
	_RELEASE(pVSBlob);
	if( FAILED( hr ) )
		return false;

	m_pImmediateContext->IASetInputLayout( m_pVertexLayout );

	ID3DBlob* pPSBlob = NULL;
	hr = m_compileshaderfromfile( L"shader.fx", "PS", "ps_4_0", &pPSBlob );
	if( FAILED( hr ) )
	{
		Log::Get()->Err("Невозможно скомпилировать файл shader.fx. Пожалуйста, запустите данную программу из папки, содержащей этот файл");
		return false;
	}

	hr = m_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &m_pPixelShader );
	_RELEASE(pPSBlob);
	if( FAILED( hr ) )
		return false;

	SimpleVertex vertices[] =
	{
		{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
		{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

		{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
		{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

		{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
		{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
		{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
		{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

		{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
		{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

		{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
		{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

		{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
		{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
		{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },
	};

В начале ничего нового, дальше описываем вершины. Как вы помните, длина текстуры от 0 до 1. Если вы впишите вместо 1, двойки, то на куб будет натянута не одна текстура а 4:) Позже попробуйте. Если написать 0.5 - то только половина текстуры.

	D3D11_BUFFER_DESC bd;
	ZeroMemory( &bd, sizeof(bd) );
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( SimpleVertex ) * 24;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory( &InitData, sizeof(InitData) );
	InitData.pSysMem = vertices;
	hr = m_pd3dDevice->CreateBuffer(&bd, &InitData, &m_pVertexBuffer);
	if( FAILED( hr ) )
		return false;

	UINT stride = sizeof( SimpleVertex );
	UINT offset = 0;
	m_pImmediateContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

	WORD indices[] =
	{
		3,1,0,
		2,1,3,

		6,4,5,
		7,4,6,

		11,9,8,
		10,9,11,

		14,12,13,
		15,12,14,

		19,17,16,
		18,17,19,

		22,20,21,
		23,20,22
	};
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( WORD ) * 36;
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = 0;
	InitData.pSysMem = indices;
	hr = m_pd3dDevice->CreateBuffer( &bd, &InitData, &m_pIndexBuffer );
	if( FAILED( hr ) )
		return false;

	m_pImmediateContext->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );

	m_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof(ConstantBuffer);
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bd.CPUAccessFlags = 0;
	hr = m_pd3dDevice->CreateBuffer(&bd, NULL, &m_pConstantBuffer);
	if( FAILED( hr ) )
		return false;

И опять ничего нового:)

	hr = D3DX11CreateShaderResourceViewFromFile( m_pd3dDevice, L"texture.png", NULL, NULL, &m_pTextureRV, NULL );
	if( FAILED( hr ) )
		return false;

	D3D11_SAMPLER_DESC sampDesc;
	ZeroMemory( &sampDesc, sizeof(sampDesc) );
	sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
	sampDesc.MinLOD = 0;
	sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
	hr = m_pd3dDevice->CreateSamplerState( &sampDesc, &m_pSamplerLinear );
	if( FAILED( hr ) )
		return false;

А вот теперь, кое-что интересное. Сначала мы используя функцию D3DX11CreateShaderResourceViewFromFile() из библиотеки d3dx11.lib грузим из файла с именем "texture.png" нашу текстуру и присваиваем ее нашему объекту m_pTextureRV.

Затем мы должны создать sample state. Сначала заполняем структуру D3D11_SAMPLER_DESC, выбирая нужную фильтрацию, адресацию и MIP. Затем через метод CreateSamplerState() мы ее создаем. Sample state описывает представление нашей текстуры.

	m_World1 = XMMatrixIdentity();
	m_World2 = XMMatrixIdentity();

	XMVECTOR Eye = XMVectorSet( 0.0f, 3.0f, -8.0f, 0.0f );
	XMVECTOR At = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
	XMVECTOR Up = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
	m_View = XMMatrixLookAtLH( Eye, At, Up );

	m_Projection = XMMatrixPerspectiveFovLH( 0.4f*3.14f, (float)640/480, 0.1f, 1000.0f);

	return true;
}

Завершаем инициализацию настройкой матриц.

bool MyRender::Draw()
{
	m_rot += .0005f;
	if(m_rot > 6.26f)
		m_rot = 0.0f;

	XMVECTOR rotaxis = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
	XMMATRIX Rotation = XMMatrixRotationAxis( rotaxis, m_rot);
	XMMATRIX Translation = XMMatrixTranslation( 0.0f, 0.0f, 4.0f );

	m_World1 = Translation * Rotation;
	
	Rotation = XMMatrixRotationAxis( rotaxis, -m_rot);
	XMMATRIX Scale = XMMatrixScaling( 1.3f, 1.3f, 1.3f );

	m_World2 = Rotation * Scale;

	XMMATRIX WVP = m_World1 * m_View * m_Projection;
	ConstantBuffer cb;
	cb.WVP = XMMatrixTranspose(WVP);	
	m_pImmediateContext->UpdateSubresource( m_pConstantBuffer, 0, NULL, &cb, 0, 0 );

	m_pImmediateContext->VSSetShader( m_pVertexShader, NULL, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffer );
	m_pImmediateContext->PSSetShader( m_pPixelShader, NULL, 0 );
	m_pImmediateContext->PSSetShaderResources( 0, 1, &m_pTextureRV );
	m_pImmediateContext->PSSetSamplers( 0, 1, &m_pSamplerLinear );
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	WVP = m_World2 * m_View * m_Projection;
	cb.WVP = XMMatrixTranspose(WVP);	
	m_pImmediateContext->UpdateSubresource( m_pConstantBuffer, 0, NULL, &cb, 0, 0 );
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	return true;
}

Здесь мы будем рисовать два куба, один вращается в центре, а другой вокруг первого. На обоих из них натянута наша текстура. Первые три строчки нужны чтобы сделать анимацию вращения. Затем мы задаем мировую матрицу первого и вторго кубов. После этого создаем описание константного буфера и передаем в шейдер уже подсчитанную матрицу первого куба. Потом подключаем шейдеры. Дальше нас интересуют вот эти две строчки:

	m_pImmediateContext->PSSetShaderResources( 0, 1, &m_pTextureRV );
	m_pImmediateContext->PSSetSamplers( 0, 1, &m_pSamplerLinear );


В первой мы подключаем шейдерный ресурс с нашей текстурой, а в следующей устаналвиваем Sampler state описывающий вывод нашей текстуры.

Дальше я думаю код понятен. Завершаем:

void MyRender::Close()
{
	_RELEASE(m_pConstantBuffer);
	_RELEASE(m_pVertexBuffer);
	_RELEASE(m_pIndexBuffer);
	_RELEASE(m_pVertexLayout);
	_RELEASE(m_pVertexShader);
	_RELEASE(m_pPixelShader);
	_RELEASE(m_pTextureRV);
	_RELEASE(m_pSamplerLinear);
}

Теперь пишем шейдер shader.fx:

cbuffer cbPerObject
{
	float4x4 WVP;
};

Texture2D ObjTexture;
SamplerState ObjSamplerState;

struct VS_OUTPUT
{
	float4 Pos : SV_POSITION;
	float2 TexCoord : TEXCOORD;
};

VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD)
{
    VS_OUTPUT output;

    output.Pos = mul(inPos, WVP);
    output.TexCoord = inTexCoord;

    return output;
}

float4 PS(VS_OUTPUT input) : SV_TARGET
{
    return ObjTexture.Sample( ObjSamplerState, input.TexCoord );
}

Здесь мы в пиксельном шейдере возвращаем пиксели текстуры.


На этом урок закончен

ИтогПравить

Скачать

Lesson 2012-11-09 18-32-03-46
Материалы сообщества доступны в соответствии с условиями лицензии CC-BY-SA , если не указано иное.