DirectX вики
Advertisement

Вступление[]

Сейчас мы узнаем о новом типе освещения - Spotlight. Пример такого освещения - фонарик или прожектор.

Если вы забыли, что это за свет, вам напомнит об этом изображение:

640px-SpotLight

Такой свет похож на точечный, но в отличие от него он испуcкает свет только в границах заданного конуса.

В примере мы успользуем Spotlight как фонарь - создадим "землю" из 9 кубов и над ними расположим источник света. 

Урок будет строится на предыдущем.


Итак, код (5 версия фреймворка):

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"

using namespace D3D11Framework;

class MyRender : public Render
{
public:
	MyRender();

	bool Init();
	bool Draw();
	void Close();

private:
	ID3D11Buffer* IndexBuffer;
	ID3D11Buffer* VertBuffer;
	ID3D11Buffer* constMatrixBuffer;
	ID3D11Buffer* constLightBuffer; 
	XMMATRIX camView;
	Shader *shader;
};

MyRender.cpp

#include "MyRender.h"

struct cbMatrixData
{
	XMMATRIX WVP;
	XMMATRIX World;
};

struct Light
{
	Light()
	{
		ZeroMemory(this, sizeof(Light));
	}

	XMFLOAT3 pos;
	float range;
	XMFLOAT3 dir;
	float cone;
	XMFLOAT3 att;
	float pad2;
	XMFLOAT4 ambient;
	XMFLOAT4 diffuse;
} light;

struct cbLightData
{
	Light light;
};

struct Vertex
{
	Vertex(float x, float y, float z, float u, float v, float nx, float ny, float nz) : pos(x,y,z), tex(u, v), normal(nx, ny, nz){}
	
	XMFLOAT3 pos;
	XMFLOAT2 tex;
	XMFLOAT3 normal;
};

MyRender::MyRender()
{
	IndexBuffer = nullptr;
	VertBuffer = nullptr;
	constMatrixBuffer = nullptr;
	constLightBuffer = nullptr;
	shader = nullptr;
}

В структуру  Light добавлен член  cone - конус света. 

bool MyRender::Init()
{
	shader = new Shader(this);
	if (!shader)
		return false;

	if ( !shader->LoadTexture(L"image.png") )
		return false;

	shader->AddInputElementDesc("POSITION", DXGI_FORMAT_R32G32B32_FLOAT);
	shader->AddInputElementDesc("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT);
	shader->AddInputElementDesc("NORMAL", DXGI_FORMAT_R32G32B32_FLOAT);
	
	if ( !shader->CreateShader(L"pointlight.vs",L"pointlight.ps") )
		return false;
	
	Vertex v[] =
	{
		Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f),
		Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f,-1.0f,  1.0f, -1.0f),
		Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f, 1.0f,  1.0f, -1.0f),
		Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f),

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

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

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

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

		Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, -1.0f),
		Vertex( 1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 1.0f,  1.0f, -1.0f),
		Vertex( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 1.0f,  1.0f,  1.0f),
		Vertex( 1.0f, -1.0f,  1.0f, 1.0f, 1.0f, 1.0f, -1.0f,  1.0f),
	};
	
	DWORD indices[] =
	{
		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
	};
	
	IndexBuffer = Buffer::CreateIndexBuffer(m_pd3dDevice, sizeof(DWORD)*36, false, indices);
	VertBuffer = Buffer::CreateVertexBuffer(m_pd3dDevice, sizeof( Vertex )*24, false, v);
	constMatrixBuffer = Buffer::CreateConstantBuffer(m_pd3dDevice, sizeof(cbMatrixData), false);
	constLightBuffer = Buffer::CreateConstantBuffer(m_pd3dDevice, sizeof(cbLightData), false);

	XMVECTOR camPosition = XMVectorSet( 0.0f, 3.0f, -8.0f, 0.0f );
	XMVECTOR camTarget = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
	XMVECTOR camUp = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
	camView = XMMatrixLookAtLH( camPosition, camTarget, camUp );

	light.pos = XMFLOAT3(0.0f, 0.0f, 0.0f);
	light.range = 100.0f;
	light.att = XMFLOAT3(0.0f, 0.2f, 0.0f);
	light.ambient = XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f);
	light.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	light.cone = 10.0f;

	return true;
}

Здесь дополнительно задаем размер конуса: light.cone = 10.0f;

bool MyRender::Draw()
{
	static float rot = 0.0f;
	static DWORD dwTimeStart = 0;
	DWORD dwTimeCur = GetTickCount();
	if( dwTimeStart == 0 )
	dwTimeStart = dwTimeCur;

	rot = ( dwTimeCur - dwTimeStart ) / 1000.0f;

	light.pos.x = 0.0f;
	light.pos.y = 0.0f-4; // Ставим "фонарь" в центр, чуть повыше уровня "земли" из 9 кубов.
	light.pos.z = 0.0f;

	light.dir.x = 0.0f - light.pos.x;
	light.dir.y = 0.0f - light.pos.y;
	light.dir.z = 0.0f - light.pos.z;
	
	UINT stride = sizeof( Vertex );
	UINT offset = 0;
	m_pImmediateContext->IASetVertexBuffers( 0, 1, &VertBuffer, &stride, &offset );
	m_pImmediateContext->IASetIndexBuffer( IndexBuffer, DXGI_FORMAT_R32_UINT, 0);
	m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	
	cbLightData cblgh;
	cblgh.light = light;
	m_pImmediateContext->UpdateSubresource( constLightBuffer, 0, NULL, &cblgh, 0, 0 );
	m_pImmediateContext->PSSetConstantBuffers(0, 1, &constLightBuffer);

	/*             Пол из 4 кубов:
		cube1World   cube4World   cube7World
		cube2World   cube5World   cube8World
		cube3World   cube6World   cube9World

	Поменяйте параматры XMMatrixTranslation, посмотрите результат.
	*/
	
	XMMATRIX cube1World = XMMatrixTranslation( -2.0f, 0.0f, 2.0f ); 
	XMMATRIX WVP = cube1World * camView * m_Projection;
	cbMatrixData cbMat;
	cbMat.World = XMMatrixTranspose(cube1World); 
	cbMat.WVP = XMMatrixTranspose(WVP); 
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube2World = XMMatrixTranslation( -2.0f, 0.0f, 0.0f );
	WVP = cube2World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube2World);
	cbMat.WVP = XMMatrixTranspose(WVP);
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube3World = XMMatrixTranslation( -2.0f, 0.0f, -2.0f ); 
	WVP = cube3World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube3World);
	cbMat.WVP = XMMatrixTranspose(WVP);
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube4World = XMMatrixTranslation( 0.0f, 0.0f, 2.0f );
	WVP = cube4World * camView * m_Projection; 
	cbMat.World = XMMatrixTranspose(cube4World);
	cbMat.WVP = XMMatrixTranspose(WVP);
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube5World = XMMatrixTranslation( 0.0f, 0.0f, 0.0f );
	WVP = cube5World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube5World); 
	cbMat.WVP = XMMatrixTranspose(WVP);
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );
	
	XMMATRIX cube6World = XMMatrixTranslation( 0.0f, 0.0f, -2.0f ); 
	WVP = cube6World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube6World);
	cbMat.WVP = XMMatrixTranspose(WVP);
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube7World = XMMatrixTranslation( 2.0f, 0.0f, 2.0f ); 
	WVP = cube7World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube7World);
	cbMat.WVP = XMMatrixTranspose(WVP);
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube8World = XMMatrixTranslation( 2.0f, 0.0f, 0.0f ); 
	WVP = cube8World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube8World);
	cbMat.WVP = XMMatrixTranspose(WVP); 
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	XMMATRIX cube9World = XMMatrixTranslation( 2.0f, 0.0f, -2.0f );
	WVP = cube9World * camView * m_Projection;
	cbMat.World = XMMatrixTranspose(cube9World);
	cbMat.WVP = XMMatrixTranspose(WVP); 
	m_pImmediateContext->UpdateSubresource( constMatrixBuffer, 0, NULL, &cbMat, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &constMatrixBuffer );
	shader->Draw();
	m_pImmediateContext->DrawIndexed( 36, 0, 0 );

	return true;
}

Рисуем 9 кубов

void MyRender::Close()
{
	_CLOSE(shader);
	_RELEASE(IndexBuffer);
	_RELEASE(VertBuffer);
	_RELEASE(constMatrixBuffer);
	_RELEASE(constLightBuffer);
}

pointlight.vs

cbuffer cbPerObject
{
	float4x4 WVP;
	float4x4 World;
};

struct VertexInputType
{
	float4 pos : POSITION;
	float2 tex : TEXCOORD;
	float3 Norm : NORMAL;
};

struct PixelInputType
{
	float4 pos : SV_POSITION;
	float2 tex : TEXCOORD;
	float3 Norm : NORMAL;
	float4 worldPos : POSITION;
};

PixelInputType VS(VertexInputType input)
{
	PixelInputType output;
	output.pos = mul(input.pos, WVP);
	output.worldPos = mul(input.pos, World);
	output.Norm = mul(input.Norm, World);
	output.tex = input.tex;
	return output;
}

pointlight.ps

Texture2D ObjTexture;
SamplerState ObjSamplerState;

struct Light
{
	float3 dir;
	float3 pos;
	float  range;
	float3 att;
	float4 ambient;
	float4 diffuse;
	float cone;
};

cbuffer cbPerFrame
{
	Light light;
};

struct PixelInputType
{
    float4 pos : SV_POSITION;
	float2 tex : TEXCOORD;
	float3 Norm : NORMAL;	
	float4 worldPos : POSITION;
};

float4 PS(PixelInputType input) : SV_TARGET
{
	float3 finalColor = float3(0.0f, 0.0f, 0.0f);

	input.Norm = normalize(input.Norm);

    float4 diffuse = ObjTexture.Sample( ObjSamplerState, input.tex );
	
	// Создаем вектор между позицией света и позицией пикселя
	float3 lightToPixelVec = light.pos - input.worldPos;
		
	// Находим расстояние между светом и пикселем (это длина вектора)
	float d = length(lightToPixelVec);
	
	// Создаем фоновый (Ambient) свет
	float3 finalAmbient = diffuse * light.ambient;

	// Если пиксель слишком далеко расположен, возвращаем цвет пикселя фонового света
	if( d > light.range )
		return float4(finalAmbient, diffuse.a);
		
	// Делаем lightToPixelVec единичным вектором, описывая направление пикселей по отношению позиции света
	lightToPixelVec /= d; 
	
	// Узнаем интенсивность света в зависимости от угла к поверхности
	float howMuchLight = dot(lightToPixelVec, input.Norm);

	// Если свет на передней поверхности
	if( howMuchLight > 0.0f )
	{	
		// Добавляем освещение к finalColor
		finalColor += howMuchLight * diffuse * light.diffuse;
		
		// Вычисляем фактор затухания
		finalColor /= light.att[0] + (light.att[1] * d) + (light.att[2] * (d*d));
		
		//Вычисляем фактор затухания от вершины то основания конуса света
		finalColor *= pow(max(dot(-lightToPixelVec, light.dir), 0.0f), light.cone);
	}	
        
	// Убеждаемся, что результат от 1 до 0, и добавляем фоновое освещение 
	finalColor = saturate(finalColor + finalAmbient);
	
	// возвращаем получившийся свет
	return float4(finalColor, diffuse.a);
}

Итог[]

Исходники - скачать

Lesson 2013-01-11 23-30-06-28
Advertisement