Instantly share code, notes, and snippets.
Save d7samurai/aee35fd5d132c51e8b0a78699cbaa1e4 to your computer and use it in GitHub Desktop.
Follow-up toMinimal D3D11, adding instanced rendering. As before: An uncluttered Direct3D 11 setup & basic rendering primer / API familiarizer. Complete, runnable Windows application contained in a single function and laid out in a linear, step-by-step fashion. No modern C++ / OOP / obscuring cruft.
The main difference here is that the hollow cube is rendered usingDrawIndexedInstanced (which saves a lot of vertices compared to the original, so model data is now small enough to be included in the source without being too much in the way), but also all trigonometry and matrix math is moved to the vertex shader, further simplifying the main code.
Each instance is merely this piece of geometry, consisting of 4 triangles:
..which is then repeated 24 times, rotated and colored:
Note: There are two separate instance buffers in play. The first one is holding the rotations that are applied to each instance, the second one contains the colors. Rotations are stored as triplets of integers, signifying how many multiples of 90 degree rotation that will be applied to each axis, in XYZ order. Colors are stored as RGB float triplets, and since each face consists of four instances, all sharing the same color, this buffer is just advanced once every four instances (hence separate buffers).
Also check outMinimal D3D11 pt3, with shadowmapping + showcasing a range of alternative setup / rendering techniques.
| #pragma comment(lib, "user32") | |
| #pragma comment(lib, "d3d11") | |
| #pragma comment(lib, "d3dcompiler") | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| #include<windows.h> | |
| #include<d3d11_1.h> | |
| #include<d3dcompiler.h> | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| #defineTITLE"Minimal D3D11 pt2 by d7samurai" | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| UINT TextureData[] = {0xffffffff,0xff7f7f7f,0xff7f7f7f,0xffffffff };// 2x2 pixels | |
| float VertexData[] =// pos.x, pos.y, pos.z, nor.x, nor.y, nor.z, tex.u, tex.v, ... | |
| { | |
| -1.00f,1.00f, -1.00f,0.0f,0.0f, -1.0f,0.0f,0.0f,1.00f,1.00f, -1.00f,0.0f,0.0f, -1.0f,9.5f,0.0f, | |
| 0.58f,0.58f, -1.00f,0.0f,0.0f, -1.0f,7.5f,2.0f, -0.58f,0.58f, -1.00f,0.0f,0.0f, -1.0f,2.0f,2.0f, | |
| -0.58f,0.58f, -1.00f,0.0f, -1.0f,0.0f,0.0f,0.0f,0.58f,0.58f, -1.00f,0.0f, -1.0f,0.0f,0.0f,0.0f, | |
| 0.58f,0.58f, -0.58f,0.0f, -1.0f,0.0f,0.0f,0.0f, -0.58f,0.58f, -0.58f,0.0f, -1.0f,0.0f,0.0f,0.0f, | |
| }; | |
| UINT IndexData[] = {0,1,3,1,2,3,4,5,7,5,6,7 }; | |
| UINT InstanceRotationData[] = {0,0,0,0,0,1,0,0,2,0,0,3,1,0,0,1,1,0,1,2,0,1,3,0,2,0,0,2,0,1,2,0,2,2,0,3,3,0,0,3,1,0,3,2,0,3,3,0,1,0,1,1,1,1,1,2,1,1,3,1,1,0,3,1,1,3,1,2,3,1,3,3 };// rot.x, rot.y, rot.z, ... in multiples of 90 degrees | |
| float InstanceColorData[] = {0.973f,0.480f,0.002f,0.897f,0.163f,0.011f,0.612f,0.000f,0.069f,0.127f,0.116f,0.408f,0.000f,0.254f,0.637f,0.001f,0.447f,0.067f };// col.r, col.g, col.b, ... | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| int WINAPIWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nShowCmd) | |
| { | |
| WNDCLASSA wndClass = {0, DefWindowProcA,0,0,0,0,0,0,0, TITLE }; | |
| RegisterClassA(&wndClass); | |
| HWND window =CreateWindowExA(0, TITLE, TITLE, WS_POPUP | WS_MAXIMIZE | WS_VISIBLE,0,0,0,0,nullptr,nullptr,nullptr,nullptr); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; | |
| ID3D11Device* baseDevice; | |
| ID3D11DeviceContext* baseDeviceContext; | |
| D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE,nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels,ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &baseDevice,nullptr, &baseDeviceContext); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3D11Device1* device; | |
| baseDevice->QueryInterface(__uuidof(ID3D11Device1),reinterpret_cast<void**>(&device)); | |
| ID3D11DeviceContext1* deviceContext; | |
| baseDeviceContext->QueryInterface(__uuidof(ID3D11DeviceContext1),reinterpret_cast<void**>(&deviceContext)); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| IDXGIDevice1* dxgiDevice; | |
| device->QueryInterface(__uuidof(IDXGIDevice1),reinterpret_cast<void**>(&dxgiDevice)); | |
| IDXGIAdapter* dxgiAdapter; | |
| dxgiDevice->GetAdapter(&dxgiAdapter); | |
| IDXGIFactory2* dxgiFactory; | |
| dxgiAdapter->GetParent(__uuidof(IDXGIFactory2),reinterpret_cast<void**>(&dxgiFactory)); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| DXGI_SWAP_CHAIN_DESC1 swapChainDesc; | |
| swapChainDesc.Width =0;// use window width | |
| swapChainDesc.Height =0;// use window height | |
| swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;// can't specify _SRGB here when using DXGI_SWAP_EFFECT_FLIP_* ...; | |
| swapChainDesc.Stereo =FALSE; | |
| swapChainDesc.SampleDesc.Count =1; | |
| swapChainDesc.SampleDesc.Quality =0; | |
| swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | |
| swapChainDesc.BufferCount =2; | |
| swapChainDesc.Scaling = DXGI_SCALING_STRETCH; | |
| swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; | |
| swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; | |
| swapChainDesc.Flags =0; | |
| IDXGISwapChain1* swapChain; | |
| dxgiFactory->CreateSwapChainForHwnd(device, window, &swapChainDesc,nullptr,nullptr, &swapChain); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3D11Texture2D* frameBuffer; | |
| swapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&frameBuffer)); | |
| D3D11_RENDER_TARGET_VIEW_DESC framebufferDesc = {}; | |
| framebufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;// ... so do this to get _SRGB swapchain | |
| framebufferDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; | |
| ID3D11RenderTargetView* frameBufferView; | |
| device->CreateRenderTargetView(frameBuffer, &framebufferDesc, &frameBufferView); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_TEXTURE2D_DESC depthBufferDesc; | |
| frameBuffer->GetDesc(&depthBufferDesc);// copy from framebuffer properties | |
| depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; | |
| depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; | |
| ID3D11Texture2D* depthBuffer; | |
| device->CreateTexture2D(&depthBufferDesc,nullptr, &depthBuffer); | |
| ID3D11DepthStencilView* depthBufferView; | |
| device->CreateDepthStencilView(depthBuffer,nullptr, &depthBufferView); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3DBlob* vsBlob; | |
| D3DCompileFromFile(L"shaders.hlsl",nullptr,nullptr,"vs_main","vs_5_0",0,0, &vsBlob,nullptr); | |
| ID3D11VertexShader* vertexShader; | |
| device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(),nullptr, &vertexShader); | |
| D3D11_INPUT_ELEMENT_DESC inputElementDesc[] =// float3 position, float3 normal, float2 texcoord, uint3 rotation, float3 color | |
| { | |
| {"POS",0, DXGI_FORMAT_R32G32B32_FLOAT,0,0, D3D11_INPUT_PER_VERTEX_DATA,0 }, | |
| {"NOR",0, DXGI_FORMAT_R32G32B32_FLOAT,0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA,0 }, | |
| {"TEX",0, DXGI_FORMAT_R32G32_FLOAT,0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA,0 }, | |
| {"ROT",0, DXGI_FORMAT_R32G32B32_UINT,1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA,1 },// advance every instance | |
| {"COL",0, DXGI_FORMAT_R32G32B32_FLOAT,2, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA,4 },// advance every 4th instance, i.e. every face | |
| }; | |
| ID3D11InputLayout* inputLayout; | |
| device->CreateInputLayout(inputElementDesc,ARRAYSIZE(inputElementDesc), vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &inputLayout); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3DBlob* psBlob; | |
| D3DCompileFromFile(L"shaders.hlsl",nullptr,nullptr,"ps_main","ps_5_0",0,0, &psBlob,nullptr); | |
| ID3D11PixelShader* pixelShader; | |
| device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(),nullptr, &pixelShader); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_RASTERIZER_DESC1 rasterizerDesc = {}; | |
| rasterizerDesc.FillMode = D3D11_FILL_SOLID; | |
| rasterizerDesc.CullMode = D3D11_CULL_BACK; | |
| ID3D11RasterizerState1* rasterizerState; | |
| device->CreateRasterizerState1(&rasterizerDesc, &rasterizerState); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_SAMPLER_DESC samplerDesc = {}; | |
| samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; | |
| samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; | |
| samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; | |
| samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; | |
| samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; | |
| ID3D11SamplerState* samplerState; | |
| device->CreateSamplerState(&samplerDesc, &samplerState); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_DEPTH_STENCIL_DESC depthStencilDesc = {}; | |
| depthStencilDesc.DepthEnable =TRUE; | |
| depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; | |
| depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS; | |
| ID3D11DepthStencilState* depthStencilState; | |
| device->CreateDepthStencilState(&depthStencilDesc, &depthStencilState); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| structfloat4 {float x, y, z, w; }; | |
| structConstants | |
| { | |
| float4 Projection[4]; | |
| float4 LightVector; | |
| float4 Rotate; | |
| float4 Scale; | |
| float4 Translate; | |
| }; | |
| D3D11_BUFFER_DESC constantBufferDesc = {}; | |
| constantBufferDesc.ByteWidth =sizeof(Constants) +0xf &0xfffffff0;// ensure constant buffer size is multiple of 16 bytes | |
| constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC; | |
| constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; | |
| constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; | |
| ID3D11Buffer* constantBuffer; | |
| device->CreateBuffer(&constantBufferDesc,nullptr, &constantBuffer); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_BUFFER_DESC vertexBufferDesc = {}; | |
| vertexBufferDesc.ByteWidth =sizeof(VertexData); | |
| vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; | |
| vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; | |
| D3D11_SUBRESOURCE_DATA vertexData = { VertexData }; | |
| ID3D11Buffer* vertexBuffer; | |
| device->CreateBuffer(&vertexBufferDesc, &vertexData, &vertexBuffer); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_BUFFER_DESC indexBufferDesc = {}; | |
| indexBufferDesc.ByteWidth =sizeof(IndexData); | |
| indexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; | |
| indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; | |
| D3D11_SUBRESOURCE_DATA indexData = { IndexData }; | |
| ID3D11Buffer* indexBuffer; | |
| device->CreateBuffer(&indexBufferDesc, &indexData, &indexBuffer); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_BUFFER_DESC instanceBufferDesc = {}; | |
| instanceBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; | |
| instanceBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; | |
| D3D11_SUBRESOURCE_DATA instanceData = {}; | |
| ID3D11Buffer* instanceRotationBuffer; | |
| instanceBufferDesc.ByteWidth =sizeof(InstanceRotationData); | |
| instanceData.pSysMem = { InstanceRotationData }; | |
| device->CreateBuffer(&instanceBufferDesc, &instanceData, &instanceRotationBuffer); | |
| ID3D11Buffer* instanceColorBuffer; | |
| instanceBufferDesc.ByteWidth =sizeof(InstanceColorData); | |
| instanceData.pSysMem = { InstanceColorData }; | |
| device->CreateBuffer(&instanceBufferDesc, &instanceData, &instanceColorBuffer); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_TEXTURE2D_DESC textureDesc = {}; | |
| textureDesc.Width =2; | |
| textureDesc.Height =2; | |
| textureDesc.MipLevels =1; | |
| textureDesc.ArraySize =1; | |
| textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; | |
| textureDesc.SampleDesc.Count =1; | |
| textureDesc.Usage = D3D11_USAGE_IMMUTABLE; | |
| textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; | |
| D3D11_SUBRESOURCE_DATA textureData = {}; | |
| textureData.pSysMem = TextureData; | |
| textureData.SysMemPitch =2 *sizeof(UINT);// texture is 2 pixels wide, 4 bytes per pixel | |
| ID3D11Texture2D* texture; | |
| device->CreateTexture2D(&textureDesc, &textureData, &texture); | |
| ID3D11ShaderResourceView* textureView; | |
| device->CreateShaderResourceView(texture,nullptr, &textureView); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| FLOAT backgroundColor[4] = {0.025f,0.025f,0.025f,1.0f }; | |
| ID3D11Buffer* buffers[] = { vertexBuffer, instanceRotationBuffer, instanceColorBuffer }; | |
| UINT strides[] = {8 *sizeof(float),3 *sizeof(UINT),3 *sizeof(float) };// vertex (float3 position, float3 normal, float2 texcoord), instance rotation (uint3 rotation), instance color (float3 color) | |
| UINT offsets[] = {0,0,0 }; | |
| D3D11_VIEWPORT viewport = {0.0f,0.0f,static_cast<float>(depthBufferDesc.Width),static_cast<float>(depthBufferDesc.Height),0.0f,1.0f }; | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| float w = viewport.Width / viewport.Height;// width (aspect ratio) | |
| float h =1.0f;// height | |
| float n =1.0f;// near | |
| float f =9.0f;// far | |
| Constants constants = {2 * n / w,0,0,0,0,2 * n / h,0,0,0,0, f / (f - n),1,0,0, n * f / (n - f),0 };// projection matrix | |
| constants.LightVector = {1.0f, -1.0f,1.0f }; | |
| constants.Rotate = {0.0f,0.0f,0.0f }; | |
| constants.Scale = {1.0f,1.0f,1.0f }; | |
| constants.Translate = {0.0f,0.0f,4.0f }; | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| while (true) | |
| { | |
| MSG msg; | |
| while (PeekMessageA(&msg,nullptr,0,0, PM_REMOVE)) | |
| { | |
| if (msg.message == WM_KEYDOWN)return0; | |
| DispatchMessageA(&msg); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| constants.Rotate.x +=0.005f; | |
| constants.Rotate.y +=0.009f; | |
| constants.Rotate.z +=0.001f; | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_MAPPED_SUBRESOURCE mappedSubresource; | |
| deviceContext->Map(constantBuffer,0, D3D11_MAP_WRITE_DISCARD,0, &mappedSubresource); | |
| *reinterpret_cast<Constants*>(mappedSubresource.pData) = constants; | |
| deviceContext->Unmap(constantBuffer,0); | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| deviceContext->ClearRenderTargetView(frameBufferView, backgroundColor); | |
| deviceContext->ClearDepthStencilView(depthBufferView, D3D11_CLEAR_DEPTH,1.0f,0); | |
| deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | |
| deviceContext->IASetInputLayout(inputLayout); | |
| deviceContext->IASetVertexBuffers(0,3, buffers, strides, offsets); | |
| deviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT,0); | |
| deviceContext->VSSetShader(vertexShader,nullptr,0); | |
| deviceContext->VSSetConstantBuffers(0,1, &constantBuffer); | |
| deviceContext->RSSetViewports(1, &viewport); | |
| deviceContext->RSSetState(rasterizerState); | |
| deviceContext->PSSetShader(pixelShader,nullptr,0); | |
| deviceContext->PSSetShaderResources(0,1, &textureView); | |
| deviceContext->PSSetSamplers(0,1, &samplerState); | |
| deviceContext->OMSetRenderTargets(1, &frameBufferView, depthBufferView); | |
| deviceContext->OMSetDepthStencilState(depthStencilState,0); | |
| deviceContext->OMSetBlendState(nullptr,nullptr,0xffffffff);// use default blend mode (i.e. disable) | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| deviceContext->DrawIndexedInstanced(ARRAYSIZE(IndexData),24,0,0,0); | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| swapChain->Present(1,0); | |
| } | |
| } |
| cbuffer constants :register(b0) | |
| { | |
| row_majorfloat4x4 projection; | |
| float3 lightvector; | |
| float3 rotate; | |
| float3 scale; | |
| float3 translate; | |
| } | |
| struct vs_in | |
| { | |
| float3 position : POS; | |
| float3 normal : NOR; | |
| float2 texcoord : TEX; | |
| uint3 rotation : ROT;// instance rotation | |
| float3 color : COL;// instance color | |
| }; | |
| struct vs_out | |
| { | |
| float4 position :SV_POSITION; | |
| float2 texcoord : TEX; | |
| float4 color : COL; | |
| }; | |
| Texture2D mytexture :register(t0); | |
| SamplerState mysampler :register(s0); | |
| float4x4get_rotation_matrix(float3 r) | |
| { | |
| float4x4 x = {1,0,0,0,0,cos(r.x), -sin(r.x),0,0,sin(r.x),cos(r.x),0,0,0,0,1 }; | |
| float4x4 y = {cos(r.y),0,sin(r.y),0,0,1,0,0, -sin(r.y),0,cos(r.y),0,0,0,0,1 }; | |
| float4x4 z = {cos(r.z), -sin(r.z),0,0,sin(r.z),cos(r.z),0,0,0,0,1,0,0,0,0,1 }; | |
| returnmul(mul(x, y), z); | |
| } | |
| vs_outvs_main(vs_in input) | |
| { | |
| float4x4 scalematrix = { scale.x,0,0,0,0, scale.y,0,0,0,0, scale.z,0,0,0,0,1 }; | |
| float4x4 translatematrix = {1,0,0,0,0,1,0,0,0,0,1,0, translate.x, translate.y, translate.z,1 }; | |
| float4x4 transform =mul(mul(mul(get_rotation_matrix(1.5708f * input.rotation),get_rotation_matrix(rotate)), scalematrix), translatematrix); | |
| float light =clamp(dot(mul(input.normal, transform),normalize(-lightvector)),0.0f,1.0f) *0.8f +0.2f; | |
| vs_out output; | |
| output.position =mul(float4(input.position,1.0f),mul(transform, projection)); | |
| output.texcoord = input.texcoord; | |
| output.color =float4(input.color * light,1.0f); | |
| return output; | |
| } | |
| float4ps_main(vs_out input) :SV_TARGET | |
| { | |
| return mytexture.Sample(mysampler, input.texcoord) * input.color; | |
| } |

