ФЭНДОМ


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

В этом уроке мы рассмотрим так называемые проецируемые текстуры. Это такие текстуры которые накладываются точнее проецируются на трехмерную поверхность. Теневые карты которые мы рассматривали ранее, могут быть одним из вариантов таких текстур. Кроме того с помочью проецируемых текстур можно создать разные световые эффекты, например лунный свет падающий из окна на пол.

Проецирование происходит из какого-то места, откуда мы, как бы светим на поверхность. 

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

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

Light.hПравить

#pragma once

#include "D3D11_Framework.h"

using namespace D3D11Framework;

class Light
{
public:
	void SetAmbientColor(float, float, float, float);
	void SetDiffuseColor(float, float, float, float);
	void SetDirection(float, float, float);

	XMFLOAT4 GetAmbientColor();
	XMFLOAT4 GetDiffuseColor();
	XMFLOAT3 GetDirection();

private:
	XMFLOAT4 m_ambientColor;
	XMFLOAT4 m_diffuseColor;
	XMFLOAT3 m_direction;
};

Light.cppПравить

#include "Light.h"

void Light::SetAmbientColor(float red, float green, float blue, float alpha)
{
	m_ambientColor = XMFLOAT4(red, green, blue, alpha);
}

void Light::SetDiffuseColor(float red, float green, float blue, float alpha)
{
	m_diffuseColor = XMFLOAT4(red, green, blue, alpha);
}

void Light::SetDirection(float x, float y, float z)
{
	m_direction = XMFLOAT3(x, y, z);
}

XMFLOAT4 Light::GetAmbientColor()
{
	return m_ambientColor;
}

XMFLOAT4 Light::GetDiffuseColor()
{
	return m_diffuseColor;
}

XMFLOAT3 Light::GetDirection()
{
	return m_direction;
}

ViewPoint.hПравить

#pragma once

#include "D3D11_Framework.h"

using namespace D3D11Framework;

class ViewPoint
{
public:
	void SetPosition(float, float, float);
	void SetLookAt(float, float, float);
	void SetProjectionParameters(float, float, float, float);

	void GenerateViewMatrix();
	void GenerateProjectionMatrix();

	void GetViewMatrix(XMMATRIX&);
	void GetProjectionMatrix(XMMATRIX&);

private:
	XMFLOAT3 m_position, m_lookAt;
	XMMATRIX m_viewMatrix, m_projectionMatrix;
	float m_fieldOfView, m_aspectRatio, m_nearPlane, m_farPlane;
};

Данный класс будет использоваться для создания места, откуда будет происходит проецирование текстуры. В реальном проекте это может быть центр окна или фонарика. Мы строим для этого места фрустум.

ViewPoint.cppПравить

#include "ViewPoint.h"

void ViewPoint::SetPosition(float x, float y, float z)
{
	m_position = XMFLOAT3(x, y, z);
}

void ViewPoint::SetLookAt(float x, float y, float z)
{
	m_lookAt = XMFLOAT3(x, y, z);
}

void ViewPoint::SetProjectionParameters(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
{
	m_fieldOfView = fieldOfView;
	m_aspectRatio = aspectRatio;
	m_nearPlane = nearPlane;
	m_farPlane = farPlane;
}

void ViewPoint::GenerateViewMatrix()
{

	XMVECTOR Pos = XMVectorSet(m_position.x, m_position.y, m_position.z, 0.0f);
	XMVECTOR LookAt = XMVectorSet( m_lookAt.x, m_lookAt.y, m_lookAt.z, 0.0f );
	XMVECTOR Up = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );

	m_viewMatrix = XMMatrixLookAtLH(Pos, LookAt, Up);
}

void ViewPoint::GenerateProjectionMatrix()
{
	m_projectionMatrix = XMMatrixPerspectiveFovLH(m_fieldOfView, m_aspectRatio, m_nearPlane, m_farPlane);
}

void ViewPoint::GetViewMatrix(XMMATRIX &viewMatrix)
{
	viewMatrix = m_viewMatrix;
}

void ViewPoint::GetProjectionMatrix(XMMATRIX &projectionMatrix)
{
	projectionMatrix = m_projectionMatrix;
}

Shader.hПравить

#pragma once

#include "D3D11_Framework.h"
#include "Light.h"
using namespace D3D11Framework;

class MyRender;

class ProjectionShader
{
public:
	ProjectionShader();

	bool Init(MyRender *render);
	void Close();
	void Render(int indexCount, CXMMATRIX worldMatrix, CXMMATRIX wvp, Light &light, CXMMATRIX wvp2, ID3D11ShaderResourceView *Texture, ID3D11ShaderResourceView *projectionTexture);
	
private:
	MyRender *m_render;

	Shader *m_shader;
	ID3D11SamplerState* m_sampleState;
	ID3D11Buffer* m_matrixBuffer;
	ID3D11Buffer* m_lightBuffer;	
};

Класс для шейдера

Shader.cppПравить

#include "Shader.h"
#include "MyRender.h"

struct MatrixBufferType
{
	XMMATRIX world;
	XMMATRIX wvp;
	XMMATRIX wvp2;
};

struct LightBufferType
{
	XMFLOAT4 ambientColor;
	XMFLOAT4 diffuseColor;
	XMFLOAT3 lightDirection;
	float padding;
};

ProjectionShader::ProjectionShader()
{
	m_shader = nullptr;
	m_sampleState = nullptr;
	m_matrixBuffer = nullptr;
	m_lightBuffer = nullptr;
}

bool ProjectionShader::Init(MyRender *render)
{
	m_render = render;
	// инициализируем шейдер и входной формат
	m_shader = new Shader(m_render);
	m_shader->AddInputElementDesc("POSITION", DXGI_FORMAT_R32G32B32_FLOAT);
	m_shader->AddInputElementDesc("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT);
	m_shader->AddInputElementDesc("NORMAL", DXGI_FORMAT_R32G32B32_FLOAT);
	if ( !m_shader->CreateShader(L"shader.vs", L"shader.ps") )
		return false;

	D3D11_SAMPLER_DESC samplerDesc;
	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;

	if( FAILED(m_render->m_pd3dDevice->CreateSamplerState(&samplerDesc, &m_sampleState)) )
		return false;

	// создаем константный буфер
	m_matrixBuffer = Buffer::CreateConstantBuffer(m_render->m_pd3dDevice, sizeof(MatrixBufferType), false);
	m_lightBuffer = Buffer::CreateConstantBuffer(m_render->m_pd3dDevice, sizeof(LightBufferType), false);	
	
	return true;
}

void ProjectionShader::Render(int indexCount, CXMMATRIX worldMatrix, CXMMATRIX wvp, Light &light, CXMMATRIX wvp2, ID3D11ShaderResourceView* Texture, ID3D11ShaderResourceView* projectionTexture)
{
	MatrixBufferType cb;
	cb.world = XMMatrixTranspose(worldMatrix);
	cb.wvp = XMMatrixTranspose(wvp);
	cb.wvp2 = XMMatrixTranspose(wvp2);

	m_render->m_pImmediateContext->UpdateSubresource(m_matrixBuffer, 0, NULL, &cb, 0, 0);
	m_render->m_pImmediateContext->VSSetConstantBuffers(0, 1, &m_matrixBuffer);
	
	LightBufferType lb;
	lb.ambientColor = light.GetAmbientColor();
	lb.diffuseColor = light.GetDiffuseColor();
	lb.lightDirection = light.GetDirection();
	lb.padding = 0.0f;
	m_render->m_pImmediateContext->UpdateSubresource(m_lightBuffer, 0, NULL, &lb, 0, 0);
	m_render->m_pImmediateContext->PSSetConstantBuffers(0, 1, &m_lightBuffer);

	m_shader->Draw();

	m_render->m_pImmediateContext->PSSetShaderResources(0, 1, &Texture);
	m_render->m_pImmediateContext->PSSetShaderResources(1, 1, &projectionTexture);
	m_render->m_pImmediateContext->PSSetSamplers(0, 1, &m_sampleState);
	m_render->m_pImmediateContext->DrawIndexed(indexCount, 0, 0);
}

void ProjectionShader::Close()
{
	_CLOSE(m_shader);
	_RELEASE(m_sampleState);
	_RELEASE(m_matrixBuffer);
	_RELEASE(m_lightBuffer);
}

MyRender.hПравить

#pragma once

#include "D3D11_Framework.h"
#include "Light.h"
#include "ViewPoint.h"
#include "Shader.h"

using namespace D3D11Framework;

class MyRender : public Render
{
public:
	MyRender();
	bool Init();
	bool Draw();
	void Close();
	
private:
	friend ProjectionShader;
	
	Camera m_cam;
	Light m_Light;
	ViewPoint m_ViewPoint;
	ProjectionShader m_shader;

	ID3D11ShaderResourceView *m_tex;

	// индексный и вершинный буферы для ящика и поверхности
	ID3D11Buffer *m_vb_ground; 
	ID3D11Buffer *m_ib_ground;
	ID3D11Buffer* m_vb_box;
	ID3D11Buffer* m_ib_box;

	// текстуры
	ID3D11ShaderResourceView* m_texture_ground;
	ID3D11ShaderResourceView* m_texture_box;

	XMFLOAT3 m_posbox;
};

MyRender.cppПравить

#include "MyRender.h"

struct Vertex
{
	Vertex(float x, float y, float z, float u, float v, float nx, float ny, float nz) : position(x,y,z), texture(u, v), normal(nx, ny, nz){}

	XMFLOAT3 position;
	XMFLOAT2 texture;
	XMFLOAT3 normal;
};

MyRender::MyRender()
{
	m_tex = nullptr;
	m_vb_ground = nullptr;
	m_ib_ground = nullptr;
	m_vb_box = nullptr;
	m_ib_box = nullptr;
	m_texture_ground = nullptr;
	m_texture_box = nullptr;
}

bool MyRender::Init()
{
	// настраиваем камеру
	m_cam.SetPos(0.0f, 5.0f, -7.0f);
	m_cam.SetRot(35.0f, 0.0f, 0.0f);

	// настраиваем свет
	m_Light.SetAmbientColor(0.15f, 0.15f, 0.15f, 1.0f);
	m_Light.SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
	m_Light.SetDirection(0.0f, -0.75f, 0.5f);

	m_shader.Init(this);

	if(FAILED(D3DX11CreateShaderResourceViewFromFile(m_pd3dDevice, L"pt.png", NULL, NULL, &m_tex, NULL)))
		return false;	

	m_ViewPoint.SetPosition(2.0f, 5.0f, -2.0f);
	m_ViewPoint.SetLookAt(0.0f, 0.0f, 0.0f);
	m_ViewPoint.SetProjectionParameters((float)(XM_PI / 2.0f), 1.0f, 0.1f, 100.0f);
	m_ViewPoint.GenerateViewMatrix();
	m_ViewPoint.GenerateProjectionMatrix();
	
	// позиция ящика
	m_posbox = XMFLOAT3(0.0f, 2.0f, 0.0f);

	// геометрия (plane)
	Vertex vert1[] =
	{
		Vertex(-5.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f),
		Vertex(5.0f, 0.0f, 5.0f, 1.0f, 0.0f,0.0f, 1.0f, 0.0f),
		Vertex(-5.0f, 0.0f, -5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f),
		Vertex(-5.0f, 0.0f, -5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f),
		Vertex(5.0f, 0.0f, 5.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f),
		Vertex(5.0f, 0.0f, -5.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f)
	};
	unsigned long indices1[6] = 
	{
		0,1,2,
		3,4,5
	};

	// создаем вершинный и индексный буферы
	m_vb_ground = Buffer::CreateVertexBuffer(m_pd3dDevice, sizeof(Vertex)*6, false, &vert1);
	m_ib_ground = Buffer::CreateIndexBuffer(m_pd3dDevice, sizeof(unsigned long)*6, false, &indices1);
	// грузим текстуру для поверхности
	D3DX11CreateShaderResourceViewFromFile(m_pd3dDevice, L"stone01.jpg", NULL, NULL, &m_texture_ground, NULL);

	// геометрия (box)
	Vertex vert2[] =
	{
		Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f),
		Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f),
		Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f),
		Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f),

		Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f),
		Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f),
		Vertex( 1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
		Vertex(-1.0f,  1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f),

		Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f),
		Vertex(-1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f),
		Vertex( 1.0f, 1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f),
		Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f),

		Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f),
		Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f),
		Vertex( 1.0f, -1.0f,  1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f),
		Vertex(-1.0f, -1.0f,  1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f),

		Vertex(-1.0f, -1.0f,  1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f),
		Vertex(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f),
		Vertex(-1.0f,  1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f),
		Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f),

		Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f),
		Vertex( 1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f),
		Vertex( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f),
		Vertex( 1.0f, -1.0f,  1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
	};

	unsigned long indices2[] =
	{
		0,  1,  2,
		0,  2,  3,

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

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

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

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

		20, 21, 22,
		20, 22, 23
	};

	m_vb_box = Buffer::CreateVertexBuffer(m_pd3dDevice, sizeof( Vertex )*24, false, vert2);
	m_ib_box = Buffer::CreateIndexBuffer(m_pd3dDevice, sizeof(unsigned long)*36, false, indices2);
	// грузим текстуры для кубов
	D3DX11CreateShaderResourceViewFromFile(m_pd3dDevice, L"wall01.jpg", NULL, NULL, &m_texture_box, NULL);	
	
	return true;
}

bool MyRender::Draw()
{		
	unsigned int stride = sizeof(Vertex); 
	unsigned int offset = 0;

	m_cam.Render();

	XMMATRIX camView = m_cam.GetViewMatrix();

	XMMATRIX viewMatrix, projMatrix;
	m_ViewPoint.GetViewMatrix(viewMatrix);
	m_ViewPoint.GetProjectionMatrix(projMatrix);

	XMMATRIX wldMatrix = XMMatrixTranslation(m_posbox.x, m_posbox.y, m_posbox.z);
	XMMATRIX WVP = wldMatrix * camView * m_Projection;
	XMMATRIX WVP2 = wldMatrix * viewMatrix * projMatrix;
	m_pImmediateContext->IASetVertexBuffers(0, 1, &m_vb_box, &stride, &offset);
	m_pImmediateContext->IASetIndexBuffer(m_ib_box, DXGI_FORMAT_R32_UINT, 0);
	m_shader.Render(36, wldMatrix, WVP, m_Light, WVP2, m_texture_box, m_tex);
		
	wldMatrix = XMMatrixTranslation(0.0f, 1.0f, 0.0f);
	WVP = wldMatrix * camView * m_Projection;
	WVP2 = wldMatrix * viewMatrix * projMatrix;
	m_pImmediateContext->IASetVertexBuffers(0, 1, &m_vb_ground, &stride, &offset);
	m_pImmediateContext->IASetIndexBuffer(m_ib_ground, DXGI_FORMAT_R32_UINT, 0);
	m_shader.Render(6, wldMatrix, WVP, m_Light, WVP2, m_texture_ground, m_tex);	
		
	return true;
}

void MyRender::Close()
{
	_RELEASE(m_tex);
	_RELEASE(m_vb_ground);
	_RELEASE(m_ib_ground);
	_RELEASE(m_vb_box);
	_RELEASE(m_ib_box);
	_RELEASE(m_texture_ground);
	_RELEASE(m_texture_box);
}

ШейдерыПравить

shader.vsПравить

cbuffer MatrixBuffer
{
	matrix world;
	matrix wvp;
	matrix wvp2;
};

struct VertexInputType
{
	float4 position : POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};

struct PixelInputType
{
	float4 position : SV_POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
	float4 viewPosition : TEXCOORD1;
};

PixelInputType VS(VertexInputType input)
{
	PixelInputType output;    
	input.position.w = 1.0f;

	output.position = mul(input.position, wvp);    
	output.viewPosition = mul(input.position, wvp2);
	output.tex = input.tex;    
	output.normal = mul(input.normal, (float3x3)world);
	output.normal = normalize(output.normal);

	return output;
}

shader.psПравить

Texture2D shaderTexture : register(t0);
Texture2D projectionTexture : register(t1);

SamplerState SampleType;

cbuffer LightBuffer
{
	float4 ambientColor;
	float4 diffuseColor;
	float3 lightDirection;
	float padding;
};

struct PixelInputType
{
	float4 position : SV_POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
	float4 viewPosition : TEXCOORD1;
};

float4 PS(PixelInputType input) : SV_TARGET
{
	// Устанавливаем цвет окружающей среды (то есть рассеяный свет)
	float4 color = ambientColor;
	// Инвертируем направление света для расчетов
	float3 lightDir = -lightDirection;
	// Вычисляем интенсивность света на этом пикселе
	float lightIntensity = saturate(dot(input.normal, lightDir));
	if(lightIntensity > 0.0f)
	{
		// Определяем цвет света на основе диффузного цвета и интенсивности света
		color += (diffuseColor * lightIntensity);
	}
	color = saturate(color);
	float4 textureColor = shaderTexture.Sample(SampleType, input.tex);
	color = color * textureColor;

	float2 projectTexCoord;
	// Вычисляем координаты проецируемой текстуры
	projectTexCoord.x =  input.viewPosition.x / input.viewPosition.w / 2.0f + 0.5f;
	projectTexCoord.y = -input.viewPosition.y / input.viewPosition.w / 2.0f + 0.5f;

	// Определяем, находятся ли спроецированные координаты в пределах от 0 до 1.
	if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
	{
		float4 projectionColor = projectionTexture.Sample(SampleType, projectTexCoord);
		// ставим цвет пикселя как цвет пикселя из текстуры которую проецируем.
		// вы также можете вместо этого смешать (умножить) данный свет, например если проецируемая текстура должна быть полупрозрачна
		color = projectionColor;
	}

	return color;
}

Второй примерПравить

Так, давайте немного изменим шейдер для того чтобы получить свет из окна

shader.psПравить

Texture2D shaderTexture : register(t0);
Texture2D projectionTexture : register(t1);

SamplerState SampleType;

cbuffer LightBuffer
{
	float4 ambientColor;
	float4 diffuseColor;
	float3 lightDirection;
	float padding;
};

struct PixelInputType
{
	float4 position : SV_POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
	float4 viewPosition : TEXCOORD1;
};

float4 PS(PixelInputType input) : SV_TARGET
{
	// Установим яркость света
	float brightness = 1.5f;

	// Инвертируем направление света для расчетов
	float3 lightDir = -lightDirection;
	// Вычисляем интенсивность света на этом пикселе
	float lightIntensity = saturate(dot(input.normal, lightDir));

	float4 color;
		
	if(lightIntensity > 0.0f)
	{
		// Определяем цвет света на основе диффузного цвета и интенсивности света
		color = (diffuseColor * lightIntensity) * brightness;
	}
	float4 textureColor = shaderTexture.Sample(SampleType, input.tex);

	float2 projectTexCoord;
	// Вычисляем координаты проецируемой текстуры
	projectTexCoord.x =  input.viewPosition.x / input.viewPosition.w / 2.0f + 0.5f;
	projectTexCoord.y = -input.viewPosition.y / input.viewPosition.w / 2.0f + 0.5f;
	
	// Определяем, находятся ли спроецированные координаты в пределах от 0 до 1.
	if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
	{
		float4 projectionColor = projectionTexture.Sample(SampleType, projectTexCoord);
		color = saturate((color * projectionColor * textureColor) + (ambientColor * textureColor));
	}
	else
	{
	    color = ambientColor * textureColor;
	}
	
	return color;
}


ВыводПравить

Первый пример:
Lesson 2013-10-20 19-29-08-71

Второй пример:

Lesson 2013-10-20 20-15-08-22
Материалы сообщества доступны в соответствии с условиями лицензии CC-BY-SA , если не указано иное.