建立了三个ConstantBuffer,一个是每一帧都需要从cpu传过来的用来旋转的world矩阵,一个是摄影机操作后传过来的view矩阵,还有一个是只传过来一次的projection矩阵和两个方向光的向量
1.lighting.fx
// Constant Buffer Variables
cbuffer CBChangesEveryFrame : register( b0 )
{
matrix World;
};
cbuffer CBChangesWhenMoving : register( b1 )
{
matrix View;
}
cbuffer CBNeverChange : register( b2 )
{
matrix Projection;
float4 vLightDir[2];
};
struct VS_INPUT
{
float4 Pos : POSITION;
float3 Norm : NORMAL;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float3 Norm : TEXCOORD0;
};
// 以上都是声明
// Vertex Shader
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output = (PS_INPUT)0;
output.Pos = mul( input.Pos, World );
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = mul( float4(input.Norm, 1), World ).xyz;// .xyz指取前三位向量,等式左边的PS_INPUT中的Norm是float3型,而右边是float4型
// 也可写成output.Norm = mul( input.Norm, (float3x3)World );
return output;
}
// Pixel Shader
float4 PS( PS_INPUT input) : SV_Target
{
static float4 vLightColor = {1.0f, 1.0f, 1.0f, 1.0f};
float4 finalColor = 0;
float k = 0.85f;// 反射率
for(int i = 0; i < 2; i++)
{
finalColor += k * saturate(dot((float3)vLightDir[i], -input.Norm) * vLightColor);// 反射率 * saturate(dot(两光线向量·顶点法线向量)·颜色值),saturate表示饱和处理——大于1变为1,小于0变成0
}
finalColor.a = 1;// 同finalColor.w = 1; rgba对应xyzw
return finalColor;
}
2.winmain.cpp
#include <windows.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
#include <directxmath.h>
#include <directxcolors.h>
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
// 找到项目的属性->链接器->输入->附加依赖项,在%(AdditionalDependencies)前追加上以下项:
// d3d11.lib;d3dcompiler.lib;dxguid.lib;winmm.lib;comctl32.lib;
// 或者像下面这样添加代码:
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "comctl32.lib")
using namespace DirectX;
struct VertexNormal
{
XMFLOAT3 Pos;
XMFLOAT3 Normal;
};
struct CBChangesEveryFrame
{
XMMATRIX mWorld;
};
struct CBChangesWhenMoving
{
XMMATRIX mView;
};
struct CBNeverChanges
{
XMMATRIX mProjection;
XMFLOAT4 vLightDir[2];
};
struct Camera
{
Camera()
: vAxisX(XMVectorSet(1.0f, 0.0f, 0.0f, 1.0f))
, vAxisAim(XMVectorSet(1.0f, 0.0f, 0.0f, 1.0f))
, vAxisY(XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f))
, vAxisZ(XMVectorSet(0.0f, 0.0f, 1.0f, 1.0f))
, vPosition(XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f))
{}
Camera(const XMVECTOR & xAxis, const XMVECTOR & yAxis, const XMVECTOR & zAxis, const XMVECTOR & pos)
: vAxisX(XMVector3Normalize(xAxis))
, vAxisAim(XMVector3Normalize(xAxis))
, vAxisY(XMVector3Normalize(yAxis))
, vAxisZ(XMVector3Normalize(zAxis))
, vPosition(pos)
{}
// 前后平移
void XMove(float dx)
{
XMMATRIX move = XMMatrixTranslationFromVector(dx * vAxisX);
vPosition = XMVector3TransformCoord(vPosition, move);
}
// 左右平移
void YMove(float dy)
{
XMMATRIX move = XMMatrixTranslationFromVector(dy * vAxisY);
vPosition = XMVector3TransformCoord(vPosition, move);
}
// 上下平移
void ZMove(float dz)
{
XMMATRIX move = XMMatrixTranslationFromVector(dz * vAxisZ);
vPosition = XMVector3TransformCoord(vPosition, move);
}
void ZSpin(float angle)
{
XMMATRIX mspin = XMMatrixRotationAxis(vAxisZ, angle);
vAxisX = XMVector3Transform(vAxisX, mspin);
vAxisAim = XMVector3Transform(vAxisAim, mspin);
vAxisY = XMVector3Transform(vAxisY, mspin);
}
void YSpin(float angle)
{
XMMATRIX mspin = XMMatrixRotationAxis(vAxisY, angle);
vAxisAim = XMVector3Transform(vAxisAim, mspin);
}
void ZoomIn(float in)
{
XMMATRIX move = XMMatrixTranslationFromVector(in * vAxisAim);
vPosition = XMVector4Transform(vPosition, move);
}
XMVECTOR vEye() const {return vPosition;}
XMVECTOR vUp() const {return XMVector4Transform(vAxisAim, XMMatrixRotationAxis(vAxisY, -XM_PIDIV2));}
XMVECTOR vFocus() const {return vPosition + vAxisAim;}
XMVECTOR vPosition;// 摄像机位矢
XMVECTOR vAxisAim;// 瞄准轴(可以在摄像机vAxisX-vAxisZ平面内上下转动)
XMVECTOR vAxisX;// 摄像机前后移动轴单位向量
XMVECTOR vAxisY;// 摄像机左右移动轴单位向量
XMVECTOR vAxisZ;// 摄像机上下移动轴单位向量
};
HINSTANCE g_hInst = nullptr;
HWND g_hWnd = nullptr;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = nullptr;
ID3D11Device1* g_pd3dDevice1 = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
ID3D11DeviceContext1* g_pImmediateContext1 = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
IDXGISwapChain1* g_pSwapChain1 = nullptr;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
ID3D11Texture2D* g_pDepthStencil = nullptr;
ID3D11DepthStencilView* g_pDepthStencilView = nullptr;
ID3D11VertexShader* g_pVertexShader = nullptr;
ID3D11PixelShader* g_pPixelShader = nullptr;
ID3D11InputLayout* g_pVertexLayout = nullptr;
ID3D11Buffer* g_pVertexBuffer = nullptr;
ID3D11Buffer* g_pCBChangesEveryFrame = nullptr;
ID3D11Buffer* g_pCBChangesWhenMoving = nullptr;
ID3D11Buffer* g_pCBNeverChange = nullptr;
Camera g_Camera;
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow );
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut );
HRESULT InitDevice();
void CleanupDevice();
void KeyDownEvent(WPARAM wParam);
void MouseEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void CameraOperator(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
void Render();
int WINAPI wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow )
{
UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );
if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;
if( FAILED( InitDevice() ) )
{
CleanupDevice();
return 0;
}
MSG msg = {};
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, nullptr, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
Render();
}
}
CleanupDevice();
UnregisterClass(L"class1", g_hInst);
return ( int )msg.wParam;
}
HRESULT InitDevice()
{
HRESULT hr = S_OK;
// 1.Create device
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 );
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
UINT numFeatureLevels = ARRAYSIZE( featureLevels );
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];
hr = D3D11CreateDevice( nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
if ( hr == E_INVALIDARG )
{
// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
hr = D3D11CreateDevice( nullptr, g_driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
D3D11_SDK_VERSION, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
}
if( SUCCEEDED( hr ) )
break;
}
if( FAILED( hr ) )
return hr;
// Obtain DXGI factory from device (since we used nullptr for pAdapter above)
IDXGIFactory1* dxgiFactory = nullptr;
{
IDXGIDevice* dxgiDevice = nullptr;
hr = g_pd3dDevice->QueryInterface( __uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice) );
if (SUCCEEDED(hr))
{
IDXGIAdapter* adapter = nullptr;
hr = dxgiDevice->GetAdapter(&adapter);
if (SUCCEEDED(hr))
{
hr = adapter->GetParent( __uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory) );
adapter->Release();
}
dxgiDevice->Release();
}
}
if (FAILED(hr))
return hr;
// 2.Create swap chain
IDXGIFactory2* dxgiFactory2 = nullptr;
hr = dxgiFactory->QueryInterface( __uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2) );
if ( dxgiFactory2 )
{
// DirectX 11.1 or later
hr = g_pd3dDevice->QueryInterface( __uuidof(ID3D11Device1), reinterpret_cast<void**>(&g_pd3dDevice1) );
if (SUCCEEDED(hr))
{
(void) g_pImmediateContext->QueryInterface( __uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&g_pImmediateContext1) );
}
DXGI_SWAP_CHAIN_DESC1 sd;
ZeroMemory(&sd, sizeof(sd));
sd.Width = width;
sd.Height = height;
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
hr = dxgiFactory2->CreateSwapChainForHwnd( g_pd3dDevice, g_hWnd, &sd, nullptr, nullptr, &g_pSwapChain1 );
if (SUCCEEDED(hr))
{
hr = g_pSwapChain1->QueryInterface( __uuidof(IDXGISwapChain), reinterpret_cast<void**>(&g_pSwapChain) );
}
dxgiFactory2->Release();
}
else
{
// DirectX 11.0 systems
DXGI_SWAP_CHAIN_DESC sd;
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 = 60;
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;
hr = dxgiFactory->CreateSwapChain( g_pd3dDevice, &sd, &g_pSwapChain );
}
// Note this tutorial doesn't handle full-screen swapchains so we block the ALT+ENTER shortcut
dxgiFactory->MakeWindowAssociation( g_hWnd, DXGI_MWA_NO_ALT_ENTER );
dxgiFactory->Release();
if (FAILED(hr))
return hr;
// 3.Create a render target view
ID3D11Texture2D* pBackBuffer = nullptr;
hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), reinterpret_cast<void**>( &pBackBuffer ) );
if( FAILED( hr ) )
return hr;
hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, nullptr, &g_pRenderTargetView );
pBackBuffer->Release();
if( FAILED( hr ) )
return hr;
// 4.Create depth stencil texture
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, nullptr, &g_pDepthStencil );
if( FAILED( hr ) )
return hr;
// 5.Create the depth stencil view
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 );
// 6.Setup the viewport
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 );
// 7.Compile and create the vertex shader. Define, create and set the input layout at the same time.
// 7.1.Compile the vertex shader
ID3DBlob* pVSBlob = nullptr;
hr = CompileShaderFromFile( L"lighting.fx", "VS", "vs_5_0", &pVSBlob );
if( FAILED( hr ) )
{
MessageBox( nullptr,
L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
return hr;
}
// 7.2.Create the vertex shader
hr = g_pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader );
if( FAILED( hr ) )
{
pVSBlob->Release();
return hr;
}
// 7.3.Define the input layout
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 },// 12是对齐字节位移,前一个元素是XMFLOAT3,大小3 * sizeof(XMFLOAT3) = 12,为了方便可用D3D11_APPEND_ALIGNED_ELEMENT可以代替12
};
UINT numElements = ARRAYSIZE( layout );
// 7.4.Create the input layout
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
pVSBlob->GetBufferSize(), &g_pVertexLayout );
pVSBlob->Release();
if( FAILED( hr ) )
return hr;
// 7.5.Set the input layout
g_pImmediateContext->IASetInputLayout( g_pVertexLayout );
// 8.Compile and create the pixel shader
// 8.1.Compile the pixel shader
ID3DBlob* pPSBlob = nullptr;
hr = CompileShaderFromFile( L"lighting.fx", "PS", "ps_5_0", &pPSBlob );
if( FAILED( hr ) )
{
MessageBox( nullptr,
L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
return hr;
}
// 8.2.Create the pixel shader
hr = g_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader );
pPSBlob->Release();
if( FAILED( hr ) )
return hr;
// 9.Create and set vertex buffer
// 9.1.Create vertex buffer
VertexNormal source[12] =
{
{XMFLOAT3(1, 0, 0), XMFLOAT3()},
{XMFLOAT3(0, 0, 1), XMFLOAT3()},
{XMFLOAT3(-1, -1, 0), XMFLOAT3()},
{XMFLOAT3(-1, 1, 0), XMFLOAT3()},
{XMFLOAT3(0, 0, 1), XMFLOAT3()},
{XMFLOAT3(1, 0, 0), XMFLOAT3()},
{XMFLOAT3(-1, -1, 0), XMFLOAT3()},
{XMFLOAT3(0, 0, 1), XMFLOAT3()},
{XMFLOAT3(-1, 1, 0), XMFLOAT3()},
{XMFLOAT3(-1, -1, 0), XMFLOAT3()},
{XMFLOAT3(-1, 1, 0), XMFLOAT3()},
{XMFLOAT3(1, 0, 0), XMFLOAT3()},
};
// 赋予各个面单位化外法向量
for (int i = 0; i < 4; i++)
{
XMVECTOR v01 = XMLoadFloat3(&source[3 * i + 1].Pos) - XMLoadFloat3(&source[3 * i].Pos);
XMVECTOR v02 = XMLoadFloat3(&source[3 * i + 2].Pos) - XMLoadFloat3(&source[3 * i].Pos);
XMVECTOR normal = XMVector3Normalize(XMVector3Cross(v01, v02));
for (int j = 0; j < 3; j++)
{
XMStoreFloat3(&source[3 * i + j].Normal, normal);
}
}
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( VertexNormal ) * 12;// 顶点数
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = source;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
//delete pVertices;
if( FAILED( hr ) )
return hr;
// 9.2.Set vertex buffer
UINT stride = sizeof( VertexNormal );
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
// 10.Create and set index buffer
// We don't need this step in this example, because the vertices are sorted to be clockwise already.
// 10.1.Create index buffer
// 10.2.Set index buffer
// 11.Set primitive topology
g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
// 12.Create the constant buffer
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(CBChangesEveryFrame);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
hr = g_pd3dDevice->CreateBuffer( &bd, nullptr, &g_pCBChangesEveryFrame );
if( FAILED( hr ) )
return hr;
bd.ByteWidth = sizeof(CBChangesWhenMoving);
hr = g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBChangesWhenMoving);
if (FAILED(hr))
return hr;
bd.ByteWidth = sizeof(CBNeverChanges);
hr = g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBNeverChange);
if (FAILED(hr))
return hr;
// 13.Initialize the world matrix, view matrix, projection matrix
CBChangesEveryFrame cb_changes_every_frame = {};
cb_changes_every_frame.mWorld = XMMatrixTranspose(XMMatrixIdentity());
g_pImmediateContext->UpdateSubresource(g_pCBChangesEveryFrame, 0, nullptr, &cb_changes_every_frame, 0, 0);
g_Camera = Camera(XMVectorSet(1.0f, 0.0f, 0.0f, 1.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f), XMVectorSet(0.0f, 0.0f, 1.0f, 1.0f), XMVectorSet(-5.0f, 0.0f, 1.0f, 1.0f));
XMMATRIX view = XMMatrixLookAtLH(g_Camera.vEye(), g_Camera.vFocus(), g_Camera.vUp());
CBChangesWhenMoving cb_changes_when_moving = {};
cb_changes_when_moving.mView = XMMatrixTranspose(view);
g_pImmediateContext->UpdateSubresource(g_pCBChangesWhenMoving, 0, nullptr, &cb_changes_when_moving, 0, 0);
CBNeverChanges cb_never_changes = {};
float nearZ = 0.01f;
float farZ = 500.0f;
XMMATRIX projection = XMMatrixPerspectiveFovLH( XM_PIDIV4, width / (FLOAT)height, nearZ, farZ );
cb_never_changes.mProjection = XMMatrixTranspose(projection);
cb_never_changes.vLightDir[0] = XMFLOAT4(0.0f, 1.0f, -0.5f, 1.0f);
cb_never_changes.vLightDir[1] = XMFLOAT4(0.0f, -1.0f, -0.5f, 1.0f);
g_pImmediateContext->UpdateSubresource(g_pCBNeverChange, 0, nullptr, &cb_never_changes, 0, 0);
return S_OK;
}
void Render()
{
static DWORD t0 = GetTickCount();
DWORD t1 = GetTickCount();
float t = (t1 - t0) / 1000.0f;
CBChangesEveryFrame cb = {};
cb.mWorld = XMMatrixTranspose(XMMatrixIdentity() * XMMatrixRotationZ(2.0f * XM_PI / 3.0f * t));// 周期3s
g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, Colors::Black);// 清屏
g_pImmediateContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);// 清depth stencil view
g_pImmediateContext->VSSetShader(g_pVertexShader, nullptr, 0);
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pCBChangesEveryFrame);// ID3D11DeviceContext::VSSetConstantBuffers()方法: Sets the constant buffers used by the vertex shader pipeline stage. 设置顶点着色管线阶段所使用的常量缓存
g_pImmediateContext->VSSetConstantBuffers(1, 1, &g_pCBChangesWhenMoving);// 如本例中,fx文件中的CBChangesWhenMoving常量缓存定义是register(b1),那么ID3D11DeviceContext::VSSetConstantBuffers()第一个参数就是1
g_pImmediateContext->VSSetConstantBuffers(2, 1, &g_pCBNeverChange);
g_pImmediateContext->PSSetShader(g_pPixelShader, nullptr, 0);
g_pImmediateContext->PSSetConstantBuffers(0, 1, &g_pCBChangesEveryFrame);// ID3D11DeviceContext::PSSetConstantBuffers()方法: Sets the constant buffers used by the pixel shader pipeline stage.
g_pImmediateContext->PSSetConstantBuffers(1, 1, &g_pCBChangesWhenMoving);
g_pImmediateContext->PSSetConstantBuffers(2, 1, &g_pCBNeverChange);
g_pImmediateContext->UpdateSubresource(g_pCBChangesEveryFrame, 0, nullptr, &cb, 0, 0);// ID3DBuffer继承于ID3DResource,一个更新一个draw
g_pImmediateContext->Draw(12, 0);// 如果用了索引缓存,用DrawIndexed()
g_pSwapChain->Present(1, 0);// 第一个参数调节刷新率
}
// 键盘按键按下事件
void KeyDownEvent(WPARAM wParam)
{
static float step = 1.0f;
switch (wParam)
{
case 'W':// 前进
{
g_Camera.XMove(step);
}
break;
case 'S':// 后退
{
g_Camera.XMove(-step);
}
break;
case 'A':// 左平移
{
g_Camera.YMove(-step);
}
break;
case 'D':// 右平移
{
g_Camera.YMove(step);
}
break;
case 'Q':// 上升
{
g_Camera.ZMove(step);
}
break;
case 'Z':// 下降
{
g_Camera.ZMove(-step);
}
break;
case VK_RIGHT:// 右转
{
g_Camera.ZSpin(5.0f / 180.0f * XM_PI);
}
break;
case VK_LEFT:// 左转
{
g_Camera.ZSpin(-5.0f / 180.0f * XM_PI);
}
break;
case VK_UP:// 仰
{
g_Camera.YSpin(-5.0f / 180.0f * XM_PI);
}
break;
case VK_DOWN:// 俯
{
g_Camera.YSpin(5.0f / 180.0f * XM_PI);
}
break;
default:
break;
}
}
// 鼠标事件
void MouseEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static bool s_bRBtnDown = false;
static POINT s_Pos0;
switch (uMsg)
{
case WM_MOUSEWHEEL:
{
static float distance = 1.0f;
if ((INT)wParam > 0)// 前滚
{
g_Camera.ZoomIn(distance);
}
else// 后滚
{
g_Camera.ZoomIn(-distance);
}
}
break;
case WM_RBUTTONDOWN:
{
s_bRBtnDown = true;
GetCursorPos(&s_Pos0);
}
break;
case WM_RBUTTONUP:
{
s_bRBtnDown = false;
}
break;
case WM_MOUSEMOVE:
{
if (s_bRBtnDown)
{
POINT Pos1;
GetCursorPos(&Pos1);
int dx = Pos1.x - s_Pos0.x;
int dy = Pos1.y - s_Pos0.y;
float degree = 1.0f;
if (dx > 0)
{
g_Camera.ZSpin(degree / 180.0f * XM_PI);
}
else if (dx < 0)
{
g_Camera.ZSpin(-degree / 180.0f * XM_PI);
}
if (dy < 0)
{
g_Camera.YSpin(-degree / 180.0f * XM_PI);
}
else if (dy > 0)
{
g_Camera.YSpin(degree / 180.0f * XM_PI);
}
s_Pos0 = Pos1;
}
}
break;
default:
break;
}
}
void CameraOperator(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
MouseEvent(hWnd, uMsg, wParam, lParam);
KeyDownEvent(wParam);
CBChangesWhenMoving cb_changes_when_moving = {};
XMMATRIX view = XMMatrixLookAtLH(g_Camera.vEye(), g_Camera.vFocus(), g_Camera.vUp());
cb_changes_when_moving.mView = XMMatrixTranspose(view);
g_pImmediateContext->UpdateSubresource(g_pCBChangesWhenMoving, 0, nullptr, &cb_changes_when_moving, 0, 0);
}
// 初始化窗口
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Register class
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_APPLICATION );
wcex.hCursor = LoadCursor( nullptr, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"class1";
wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_APPLICATION );
if( !RegisterClassEx( &wcex ) )
return E_FAIL;
// Create window
g_hInst = hInstance;
RECT rc = { 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
g_hWnd = CreateWindow( L"class1", L"简单四面体方向光渲染",
WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInstance,
nullptr );
if( !g_hWnd )
return E_FAIL;
ShowWindow( g_hWnd, nCmdShow );
return S_OK;
}
void CleanupDevice()
{
if (g_pImmediateContext) g_pImmediateContext->ClearState();
if (g_pCBChangesEveryFrame) g_pCBChangesEveryFrame->Release();
if (g_pCBChangesWhenMoving) g_pCBChangesWhenMoving->Release();
if (g_pCBNeverChange) g_pCBNeverChange->Release();
if (g_pVertexBuffer) g_pVertexBuffer->Release();
if (g_pVertexLayout) g_pVertexLayout->Release();
if (g_pVertexShader) g_pVertexShader->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_pSwapChain1) g_pSwapChain1->Release();
if (g_pSwapChain) g_pSwapChain->Release();
if (g_pImmediateContext1) g_pImmediateContext1->Release();
if (g_pImmediateContext) g_pImmediateContext->Release();
if (g_pd3dDevice1) g_pd3dDevice1->Release();
if (g_pd3dDevice) g_pd3dDevice->Release();
}
// 回调函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
case WM_KEYDOWN:
{
if (wParam == VK_ESCAPE)
{
if (MessageBox(g_hWnd, L"确认退出?", L"提醒", MB_OKCANCEL) == IDOK)
{
SendMessage(g_hWnd, WM_DESTROY, 0, 0);
}
}
CameraOperator(hWnd, uMsg, wParam, lParam);
}
break;
case WM_MOUSEMOVE:
{
CameraOperator(hWnd, uMsg, wParam, lParam);
}
break;
case WM_RBUTTONDOWN:
{
CameraOperator(hWnd, uMsg, wParam, lParam);
}
break;
case WM_RBUTTONUP:
{
CameraOperator(hWnd, uMsg, wParam, lParam);
}
break;
case WM_MOUSEWHEEL:
{
CameraOperator(hWnd, uMsg, wParam, lParam);
}
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
// 从文件编译着色器,由微软官网例子提供
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
HRESULT hr = S_OK;
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
// Setting this flag improves the shader debugging experience, but still allows
// the shaders to be optimized and to run exactly the way they will run in
// the release configuration of this program.
dwShaderFlags |= D3DCOMPILE_DEBUG;
// Disable optimizations to further improve shader debugging
dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
ID3DBlob* pErrorBlob = nullptr;
hr = D3DCompileFromFile( szFileName, nullptr, nullptr, szEntryPoint, szShaderModel,
dwShaderFlags, 0, ppBlobOut, &pErrorBlob );
if( FAILED(hr) )
{
if( pErrorBlob )
{
OutputDebugStringA( reinterpret_cast<const char*>( pErrorBlob->GetBufferPointer() ) );
pErrorBlob->Release();
}
return hr;
}
if( pErrorBlob ) pErrorBlob->Release();
return S_OK;
}
3. 效果图: