Skip to content

Instantly share code, notes, and snippets.

@krupitskas
Created October 28, 2014 13:29
Show Gist options
  • Save krupitskas/9f8ab00908262789af98 to your computer and use it in GitHub Desktop.
Save krupitskas/9f8ab00908262789af98 to your computer and use it in GitHub Desktop.
//--------------------------------------------------------------------------------------
// Урок 4. Нормали и простейший свет. Основан на примере из DX SDK (c) Microsoft Corp.
//--------------------------------------------------------------------------------------
#include <windows.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dcompiler.h>
#include <xnamath.h>
#include "resource.h"
#define MX_SETWORLD 0x101
#define MX_SETWORLD2 0x102
//--------------------------------------------------------------------------------------
// Структуры
//--------------------------------------------------------------------------------------
// Структура вершины
struct SimpleVertex
{
XMFLOAT3 Pos; // Координаты точки в пространстве
XMFLOAT3 Normal; // Нормаль вершины
};
// Структура константного буфера (совпадает со структурой в шейдере)
struct ConstantBuffer
{
XMMATRIX mWorld; // Матрица мира
XMMATRIX mView; // Матрица вида
XMMATRIX mProjection; // Матрица проекции
XMFLOAT4 vLightDir[3]; // Направление света
XMFLOAT4 vLightColor[3];// Цвет источника
XMFLOAT4 vOutputColor; // Активный цвет (для второго PSSolid)
};
//--------------------------------------------------------------------------------------
// Глобальные переменные
//--------------------------------------------------------------------------------------
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = NULL; // Устройство (для создания объектов)
ID3D11DeviceContext* g_pImmediateContext = NULL; // Контекст (устройство рисования)
IDXGISwapChain* g_pSwapChain = NULL; // Цепь связи (буфера с экраном)
ID3D11RenderTargetView* g_pRenderTargetView = NULL; // Объект вида, задний буфер
ID3D11Texture2D* g_pDepthStencil = NULL; // Текстура буфера глубин
ID3D11DepthStencilView* g_pDepthStencilView = NULL; // Объект вида, буфер глубин
ID3D11VertexShader* g_pVertexShader = NULL; // Вершинный шейдер
ID3D11PixelShader* g_pPixelShader = NULL; // Пиксельный шейдер для куба
ID3D11PixelShader* g_pPixelShaderSolid = NULL; // Пиксельный шейдер для источников света
ID3D11InputLayout* g_pVertexLayout = NULL; // Описание формата вершин
ID3D11Buffer* g_pVertexBuffer = NULL; // Буфер вершин
ID3D11Buffer* g_pIndexBuffer = NULL; // Буфер индексов вершин
ID3D11Buffer* g_pConstantBuffer = NULL; // Константный буфер
XMMATRIX g_World; // Матрица мира
XMMATRIX g_View; // Матрица вида
XMMATRIX g_Projection; // Матрица проекции
FLOAT t = 0.0f; // Переменная-время
XMFLOAT4 vLightDirs[3]; // Направление света (позиция источников)
XMFLOAT4 vLightColors[3]; // Цвет источников
XMFLOAT4 vCubes[2]; // Количество кубиков
//--------------------------------------------------------------------------------------
// Предварительные объявления функций
//--------------------------------------------------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); // Создание окна
HRESULT InitDevice(); // Инициализация устройств DirectX
HRESULT InitGeometry(); // Инициализация шаблона ввода и буфера вершин
HRESULT InitMatrixes(); // Инициализация матриц
void UpdateLight(); // Обновление параметров света
void UpdateMatrix(UINT nLightIndex); // Обновление матрицы мира
void Render(); // Функция рисования
void CleanupDevice(); // Удаление созданнных устройств DirectX
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); // Функция окна
//--------------------------------------------------------------------------------------
// Точка входа в программу. Инициализация всех объектов и вход в цикл сообщений.
// Свободное время используется для отрисовки сцены.
//--------------------------------------------------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );
// Создание окна приложения
if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;
// Создание объектов DirectX
if( FAILED( InitDevice() ) )
{
CleanupDevice();
return 0;
}
// Создание шейдеров и буфера вершин
if( FAILED( InitGeometry() ) )
{
CleanupDevice();
return 0;
}
// Инициализация матриц
if( FAILED( InitMatrixes() ) )
{
CleanupDevice();
return 0;
}
// Главный цикл сообщений
MSG msg = {0};
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
Render(); // Рисуем сцену
}
}
// Освобождаем объекты DirectX
CleanupDevice();
return ( int )msg.wParam;
}
//--------------------------------------------------------------------------------------
// Регистрация класса и создание окна
//--------------------------------------------------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Регистрация класса
WNDCLASSEX wcex;
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_ICON1 );
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"Urok5WindowClass";
wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_ICON1 );
if( !RegisterClassEx( &wcex ) )
return E_FAIL;
// Создание окна
g_hInst = hInstance;
RECT rc = { 0, 0, 400, 300 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
g_hWnd = CreateWindow( L"Urok5WindowClass", L"Урок 5. Нормали и простейший свет", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance,
NULL );
if( !g_hWnd )
return E_FAIL;
ShowWindow( g_hWnd, nCmdShow );
return S_OK;
}
//--------------------------------------------------------------------------------------
// Вызывается каждый раз, когда приложение получает системное сообщение
//--------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;
switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
default:
return DefWindowProc( hWnd, message, wParam, lParam );
}
return 0;
}
//--------------------------------------------------------------------------------------
// Вспомогательная функция для компиляции шейдеров в D3DX11
//--------------------------------------------------------------------------------------
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
HRESULT hr = S_OK;
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
ID3DBlob* pErrorBlob;
hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel,
dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
if( FAILED(hr) )
{
if( pErrorBlob != NULL )
OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
if( pErrorBlob ) pErrorBlob->Release();
return hr;
}
if( pErrorBlob ) pErrorBlob->Release();
return S_OK;
}
//--------------------------------------------------------------------------------------
// Создание устройства Direct3D (D3D Device), связующей цепи (Swap Chain) и
// контекста устройства (Immediate Context).
//--------------------------------------------------------------------------------------
HRESULT InitDevice()
{
HRESULT hr = S_OK;
RECT rc;
GetClientRect( g_hWnd, &rc );
UINT width = rc.right - rc.left; // получаем ширину
UINT height = rc.bottom - rc.top; // и высоту окна
UINT createDeviceFlags = 0;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT numDriverTypes = ARRAYSIZE( driverTypes );
// Тут мы создаем список поддерживаемых версий DirectX
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
UINT numFeatureLevels = ARRAYSIZE( featureLevels );
// Сейчас мы создадим устройства DirectX. Для начала заполним структуру,
// которая описывает свойства переднего буфера и привязывает его к нашему окну.
DXGI_SWAP_CHAIN_DESC sd; // Структура, описывающая цепь связи (Swap Chain)
ZeroMemory( &sd, sizeof( sd ) ); // очищаем ее
sd.BufferCount = 1; // у нас один буфер
sd.BufferDesc.Width = width; // ширина буфера
sd.BufferDesc.Height = height; // высота буфера
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // формат пикселя в буфере
sd.BufferDesc.RefreshRate.Numerator = 75; // частота обновления экрана
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // назначение буфера - задний буфер
sd.OutputWindow = g_hWnd; // привязываем к нашему окну
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE; // не полноэкранный режим
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];
hr = D3D11CreateDeviceAndSwapChain( NULL, g_driverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
if (SUCCEEDED(hr)) // Если устройства созданы успешно, то выходим из цикла
break;
}
if (FAILED(hr)) return hr;
// Теперь создаем задний буфер. Обратите внимание, в SDK
// RenderTargetOutput - это передний буфер, а RenderTargetView - задний.
// Извлекаем описание заднего буфера
ID3D11Texture2D* pBackBuffer = NULL;
hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
if (FAILED(hr)) return hr;
// По полученному описанию создаем поверхность рисования
hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
if (FAILED(hr)) return hr;
// Переходим к созданию буфера глубин
// Создаем текстуру-описание буфера глубин
D3D11_TEXTURE2D_DESC descDepth; // Структура с параметрами
ZeroMemory( &descDepth, sizeof(descDepth) );
descDepth.Width = width; // ширина и
descDepth.Height = height; // высота текстуры
descDepth.MipLevels = 1; // уровень интерполяции
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; // формат (размер пикселя)
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; // вид - буфер глубин
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
// При помощи заполненной структуры-описания создаем объект текстуры
hr = g_pd3dDevice->CreateTexture2D( &descDepth, NULL, &g_pDepthStencil );
if (FAILED(hr)) return hr;
// Теперь надо создать сам объект буфера глубин
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV; // Структура с параметрами
ZeroMemory( &descDSV, sizeof(descDSV) );
descDSV.Format = descDepth.Format; // формат как в текстуре
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
// При помощи заполненной структуры-описания и текстуры создаем объект буфера глубин
hr = g_pd3dDevice->CreateDepthStencilView( g_pDepthStencil, &descDSV, &g_pDepthStencilView );
if (FAILED(hr)) return hr;
// Подключаем объект заднего буфера и объект буфера глубин к контексту устройства
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, g_pDepthStencilView );
// Установки вьюпорта (масштаб и система координат). В предыдущих версиях он создавался
// автоматически, если не был задан явно.
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)width;
vp.Height = (FLOAT)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pImmediateContext->RSSetViewports( 1, &vp );
return S_OK;
}
//--------------------------------------------------------------------------------------
// Создание буфера вершин, шейдеров (shaders) и описания формата вершин (input layout)
//--------------------------------------------------------------------------------------
HRESULT InitGeometry()
{
HRESULT hr = S_OK;
// Компиляция вершинного шейдера из файла
ID3DBlob* pVSBlob = NULL; // Вспомогательный объект - просто место в оперативной памяти
hr = CompileShaderFromFile( L"urok5.fx", "VS", "vs_4_0", &pVSBlob );
if( FAILED( hr ) )
{
MessageBox( NULL, L"Невозможно скомпилировать файл FX. Пожалуйста, запустите данную программу из папки, содержащей файл FX.", L"Ошибка", MB_OK );
return hr;
}
// Создание вершинного шейдера
hr = g_pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader );
if( FAILED( hr ) )
{
pVSBlob->Release();
return hr;
}
// Определение шаблона вершин
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = ARRAYSIZE( layout );
// Создание шаблона вершин
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
pVSBlob->GetBufferSize(), &g_pVertexLayout );
pVSBlob->Release();
if (FAILED(hr)) return hr;
// Подключение шаблона вершин
g_pImmediateContext->IASetInputLayout( g_pVertexLayout );
// Компиляция пиксельного шейдера для основного большого куба из файла
ID3DBlob* pPSBlob = NULL;
hr = CompileShaderFromFile( L"urok5.fx", "PS", "ps_4_0", &pPSBlob );
if( FAILED( hr ) )
{
MessageBox( NULL, L"Невозможно скомпилировать файл FX. Пожалуйста, запустите данную программу из папки, содержащей файл FX.", L"Ошибка", MB_OK );
return hr;
}
// Создание пиксельного шейдера
hr = g_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader );
pPSBlob->Release();
if (FAILED(hr))
return hr;
// Компиляция пиксельного шейдера для источников света из файла
pPSBlob = NULL;
hr = CompileShaderFromFile( L"urok5.fx", "PSSolid", "ps_4_0", &pPSBlob );
if( FAILED( hr ) )
{
MessageBox( NULL, L"Невозможно скомпилировать файл FX. Пожалуйста, запустите данную программу из папки, содержащей файл FX.", L"Ошибка", MB_OK );
return hr;
}
// Создание пиксельного шейдера
hr = g_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShaderSolid );
pPSBlob->Release();
if (FAILED(hr)) return hr;
// Создание буфера вершин (по 4 точки на каждую сторону куба, всего 24 вершины)
SimpleVertex vertices[] =
{ /* координаты X, Y, Z нормаль X, Y, Z */
{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT3( 0.0f, 1.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT3( 0.0f, 1.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT3( 0.0f, 1.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT3( 0.0f, 1.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT3( 0.0f, -1.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT3( 0.0f, -1.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT3( 0.0f, -1.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT3( 0.0f, -1.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT3( -1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT3( -1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT3( -1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT3( -1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT3( 1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT3( 1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT3( 1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT3( 1.0f, 0.0f, 0.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT3( 0.0f, 0.0f, -1.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT3( 0.0f, 0.0f, -1.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT3( 0.0f, 0.0f, -1.0f ) },
{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT3( 0.0f, 0.0f, -1.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT3( 0.0f, 0.0f, 1.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT3( 0.0f, 0.0f, 1.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT3( 0.0f, 0.0f, 1.0f ) },
{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT3( 0.0f, 0.0f, 1.0f ) },
};
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; // указатель на наши 8 вершин
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
if (FAILED(hr)) return hr;
// Создание буфера индексов
// 1) cоздание массива с данными
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
};
// 2) cоздание объекта буфера
bd.Usage = D3D11_USAGE_DEFAULT; // Структура, описывающая создаваемый буфер
bd.ByteWidth = sizeof( WORD ) * 36; // 36 вершин для 12 треугольников (6 сторон)
bd.BindFlags = D3D11_BIND_INDEX_BUFFER; // тип - буфер индексов
bd.CPUAccessFlags = 0;
InitData.pSysMem = indices; // указатель на наш массив индексов
// Вызов метода g_pd3dDevice создаст объект буфера индексов
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pIndexBuffer );
if (FAILED(hr)) return hr;
// Установка буфера вершин
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
// Установка буфера индексов
g_pImmediateContext->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
// Установка способа отрисовки вершин в буфере
g_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 = g_pd3dDevice->CreateBuffer( &bd, NULL, &g_pConstantBuffer );
if (FAILED(hr)) return hr;
return S_OK;
}
//--------------------------------------------------------------------------------------
// Инициализация матриц
//--------------------------------------------------------------------------------------
HRESULT InitMatrixes()
{
RECT rc;
GetClientRect( g_hWnd, &rc );
UINT width = rc.right - rc.left; // получаем ширину
UINT height = rc.bottom - rc.top; // и высоту окна
// Инициализация матрицы мира
g_World = XMMatrixIdentity();
// Инициализация матрицы вида
XMVECTOR Eye = XMVectorSet( 0.0f, 4.0f, -20.0f, 0.0f ); // Откуда смотрим
XMVECTOR At = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ); // Куда смотрим
XMVECTOR Up = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ); // Направление верха
g_View = XMMatrixLookAtLH( Eye, At, Up );
// Инициализация матрицы проекции
// Параметры: 1) ширина угла объектива 2) "квадратность" пикселя
// 3) самое ближнее видимое расстояние 4) самое дальнее видимое расстояние
g_Projection = XMMatrixPerspectiveFovLH( XM_PIDIV4, width / (FLOAT)height, 0.01f, 100.0f );
return S_OK;
}
//--------------------------------------------------------------------------------------
// Вычисляем направление света
//--------------------------------------------------------------------------------------
void UpdateLight()
{
// Обновление переменной-времени
if( g_driverType == D3D_DRIVER_TYPE_REFERENCE )
{
t += ( float )XM_PI * 0.0125f;
}
else
{
static DWORD dwTimeStart = 0;
DWORD dwTimeCur = GetTickCount();
if( dwTimeStart == 0 )
dwTimeStart = dwTimeCur;
t = ( dwTimeCur - dwTimeStart ) / 1000.0f;
}
// Задаем начальные координаты источников света
vLightDirs[0] = XMFLOAT4( -0.577f, 0.577f, -0.577f, 1.0f );
vLightDirs[1] = XMFLOAT4( 0.0f, 0.0f, -1.0f, 1.0f );
vLightDirs[2] = XMFLOAT4( 0.0f, 0.0f, -2.0f, 1.0f);
// Задаем цвет источников света, у нас он не будет меняться
vLightColors[0] = XMFLOAT4( 0.0f, 1.0f, 0.0f, 1.0f );
vLightColors[1] = XMFLOAT4( 0.0f, 0.0f, 1.0f, 1.0f );
vLightColors[2] = XMFLOAT4( 1.0f, 0.0f, 0.0f, 1.0f);
// При помощи трансформаций поворачиваем второй источник света
XMMATRIX mRotate = XMMatrixRotationY( -2.0f * t );
XMVECTOR vLightDir = XMLoadFloat4( &vLightDirs[1] );
vLightDir = XMVector3Transform( vLightDir, mRotate );
XMStoreFloat4( &vLightDirs[1], vLightDir );
// При помощи трансформаций поворачиваем первый источник света
mRotate = XMMatrixRotationY( 0.5f * t );
vLightDir = XMLoadFloat4( &vLightDirs[0] );
vLightDir = XMVector3Transform( vLightDir, mRotate );
XMStoreFloat4( &vLightDirs[0], vLightDir );
// При помощи трансформаций поворачиваем третий источник света
mRotate = XMMatrixRotationX(1.0f * t);
vLightDir = XMLoadFloat4(&vLightDirs[2]);
vLightDir = XMVector3Transform(vLightDir, mRotate);
XMStoreFloat4(&vLightDirs[2], vLightDir);
}
//--------------------------------------------------------------------------------------
// Устанавливаем матрицы для текущего источника света (0-1) или мира (MX_SETWORLD)
//--------------------------------------------------------------------------------------
void UpdateMatrix(UINT nLightIndex)
{
// Небольшая проверка индекса
if (nLightIndex == MX_SETWORLD) {
// Если рисуем центральный куб: его надо просто медленно вращать
g_World = XMMatrixRotationAxis( XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), t );
nLightIndex = 0;
}
else if (nLightIndex == MX_SETWORLD2) {
XMMATRIX pos = XMMatrixIdentity();
pos = XMMatrixTranslation(3.0f, 2.0f, 0.0f);
// Если рисуем центральный куб: его надо просто медленно вращать
g_World = pos * XMMatrixRotationAxis(XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), t);
nLightIndex = 0;
}
else if (nLightIndex < 3) {
// Если рисуем источники света: перемещаем матрицу в точку и уменьшаем в 5 раз
g_World = XMMatrixTranslationFromVector( 5.0f * XMLoadFloat4( &vLightDirs[nLightIndex] ) );
XMMATRIX mLightScale = XMMatrixScaling( 0.1f, 0.1f, 0.1f );
g_World = mLightScale * g_World;
}
else {
nLightIndex = 0;
}
// Обновление содержимого константного буфера
ConstantBuffer cb1; // временный контейнер
cb1.mWorld = XMMatrixTranspose( g_World ); // загружаем в него матрицы
cb1.mView = XMMatrixTranspose( g_View );
cb1.mProjection = XMMatrixTranspose( g_Projection );
cb1.vLightDir[0] = vLightDirs[0]; // загружаем данные о свете
cb1.vLightDir[1] = vLightDirs[1];
cb1.vLightDir[2] = vLightDirs[2];
cb1.vLightColor[0] = vLightColors[0];
cb1.vLightColor[1] = vLightColors[1];
cb1.vLightColor[2] = vLightColors[2];
cb1.vOutputColor = vLightColors[nLightIndex];
g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb1, 0, 0 );
}
//--------------------------------------------------------------------------------------
// Рендеринг кадра
//--------------------------------------------------------------------------------------
void Render()
{
// Очищаем задний буфер в синий цвет
float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
// Очищаем буфер глубин до едицины (максимальная глубина)
g_pImmediateContext->ClearDepthStencilView( g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );
UpdateLight(); // Устанвока освещения
// Рисуем центральный куб
// 1) Установка матрицы центрального куба
UpdateMatrix(MX_SETWORLD);
// 2) Устанавливаем шейдеры и константные буферы
g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pConstantBuffer );
g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
g_pImmediateContext->PSSetConstantBuffers( 0, 1, &g_pConstantBuffer );
// 3) Рисуем в заднем буфере 36 вершин
g_pImmediateContext->DrawIndexed( 36, 0, 0 );
// Рисуем центральный куб
// 1) Установка матрицы центрального куба
UpdateMatrix(MX_SETWORLD2);
// 2) Устанавливаем шейдеры и константные буферы
g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0);
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pConstantBuffer);
g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0);
g_pImmediateContext->PSSetConstantBuffers(0, 1, &g_pConstantBuffer);
// 3) Рисуем в заднем буфере 36 вершин
g_pImmediateContext->DrawIndexed(36, 0, 0);
// Рисуем все источники света
// 1) Устанавливаем пиксельный шейдер
g_pImmediateContext->PSSetShader( g_pPixelShaderSolid, NULL, 0 );
for( int m = 0; m < 3; m++ )
{
// 2) Устанавливаем матрицу мира источника света
UpdateMatrix( m );
// 3) Рисуем в заднем буфере 36 вершин
g_pImmediateContext->DrawIndexed( 36, 0, 0 );
}
// Копируем задний буфер в переднйи (на экран)
g_pSwapChain->Present( 0, 0 );
}
//--------------------------------------------------------------------------------------
// Освобождение всех созданных объектов
//--------------------------------------------------------------------------------------
void CleanupDevice()
{
// Сначала отключим контекст устройства
if( g_pImmediateContext ) g_pImmediateContext->ClearState();
// Потом удалим объекты
if( g_pConstantBuffer ) g_pConstantBuffer->Release();
if( g_pVertexBuffer ) g_pVertexBuffer->Release();
if( g_pIndexBuffer ) g_pIndexBuffer->Release();
if( g_pVertexLayout ) g_pVertexLayout->Release();
if( g_pVertexShader ) g_pVertexShader->Release();
if( g_pPixelShaderSolid ) g_pPixelShaderSolid->Release();
if( g_pPixelShader ) g_pPixelShader->Release();
if( g_pDepthStencil ) g_pDepthStencil->Release();
if( g_pDepthStencilView ) g_pDepthStencilView->Release();
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pImmediateContext ) g_pImmediateContext->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment