Вступление[]
Сейчас мы узнаем о новом типе освещения - 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); }
Итог[]
Исходники - скачать