Skip to content

Instantly share code, notes, and snippets.

@LVutner
Forked from d7samurai/.readme.md
Created October 23, 2022 17:05
Show Gist options
  • Save LVutner/e0dc5e32567b0b82a56f1e0dfef062b6 to your computer and use it in GitHub Desktop.
Save LVutner/e0dc5e32567b0b82a56f1e0dfef062b6 to your computer and use it in GitHub Desktop.
Minimal D3D11 pt3

Minimal D3D11 pt3

An elaboration on Minimal D3D11 pt2, adding shadowmapping and incorporating various improvements and alternative approaches to the rendering setup, such as manual vertex fetching, samplerless texture lookup, null shader depth map rendering and procedurally generated texture and instance data. As before, this is intended to be an uncluttered Direct3D 11 setup & basic rendering primer / API familiarizer, in the form of a 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. View on YouTube

minimald3d11pt3

#pragma comment(lib, "user32")
#pragma comment(lib, "d3d11")
#pragma comment(lib, "d3dcompiler")
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
///////////////////////////////////////////////////////////////////////////////////////////////////
#define TITLE "Minimal D3D11 pt3 by d7samurai"
///////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
WNDCLASSEXA wndClassEx = { sizeof(wndClassEx) };
wndClassEx.lpfnWndProc = DefWindowProcA;
wndClassEx.lpszClassName = TITLE;
RegisterClassExA(&wndClassEx);
HWND window = CreateWindowExA(0, TITLE, TITLE, WS_POPUP | WS_MAXIMIZE | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 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.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;
IDXGISwapChain1* swapChain;
dxgiFactory->CreateSwapChainForHwnd(device, window, &swapChainDesc, nullptr, nullptr, &swapChain);
///////////////////////////////////////////////////////////////////////////////////////////////
ID3D11Texture2D* framebufferTexture;
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&framebufferTexture));
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* framebufferRTV;
device->CreateRenderTargetView(framebufferTexture, &framebufferDesc, &framebufferRTV);
///////////////////////////////////////////////////////////////////////////////////////////////
D3D11_TEXTURE2D_DESC framebufferDepthDesc;
framebufferTexture->GetDesc(&framebufferDepthDesc); // copy framebuffer properties
framebufferDepthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
framebufferDepthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
ID3D11Texture2D* framebufferDepthTexture;
device->CreateTexture2D(&framebufferDepthDesc, nullptr, &framebufferDepthTexture);
ID3D11DepthStencilView* framebufferDSV;
device->CreateDepthStencilView(framebufferDepthTexture, nullptr, &framebufferDSV);
///////////////////////////////////////////////////////////////////////////////////////////////
D3D11_TEXTURE2D_DESC shadowmapDepthDesc = {};
shadowmapDepthDesc.Width = 2048;
shadowmapDepthDesc.Height = 2048;
shadowmapDepthDesc.MipLevels = 1;
shadowmapDepthDesc.ArraySize = 1;
shadowmapDepthDesc.Format = DXGI_FORMAT_R32_TYPELESS;
shadowmapDepthDesc.SampleDesc.Count = 1;
shadowmapDepthDesc.Usage = D3D11_USAGE_DEFAULT;
shadowmapDepthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
ID3D11Texture2D* shadowmapDepthTexture;
device->CreateTexture2D(&shadowmapDepthDesc, nullptr, &shadowmapDepthTexture);
D3D11_DEPTH_STENCIL_VIEW_DESC shadowmapDSVdesc = {};
shadowmapDSVdesc.Format = DXGI_FORMAT_D32_FLOAT;
shadowmapDSVdesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
ID3D11DepthStencilView* shadowmapDSV;
device->CreateDepthStencilView(shadowmapDepthTexture, &shadowmapDSVdesc, &shadowmapDSV);
D3D11_SHADER_RESOURCE_VIEW_DESC shadowmapSRVdesc = {};
shadowmapSRVdesc.Format = DXGI_FORMAT_R32_FLOAT;
shadowmapSRVdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shadowmapSRVdesc.Texture2D.MipLevels = 1;
ID3D11ShaderResourceView* shadowmapSRV;
device->CreateShaderResourceView(shadowmapDepthTexture, &shadowmapSRVdesc, &shadowmapSRV);
///////////////////////////////////////////////////////////////////////////////////////////////
struct float4 { float x, y, z, w; };
struct Constants
{
float4 CameraProjection[4];
float4 LightProjection[4];
float4 LightRotation;
float4 ModelRotation;
float4 ModelTranslation;
float4 ShadowmapSize;
};
D3D11_BUFFER_DESC constantBufferDesc = {};
constantBufferDesc.ByteWidth = sizeof(Constants) + 0xf & 0xfffffff0; // round constant buffer size up to 16 byte boundary
constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
ID3D11Buffer* constantBuffer;
device->CreateBuffer(&constantBufferDesc, nullptr, &constantBuffer);
///////////////////////////////////////////////////////////////////////////////////////////////
float vertexData[] = { -1, 1, -1, 0, 0, 1, 1, -1, 9.5f, 0, -0.58f, 0.58f, -1, 2, 2, 0.58f, 0.58f, -1, 7.5f, 2, -0.58f, 0.58f, -1, 0, 0, 0.58f, 0.58f, -1, 0, 0, -0.58f, 0.58f, -0.58f, 0, 0, 0.58f, 0.58f, -0.58f, 0, 0 }; // float3 position, float2 texcoord, ...
D3D11_BUFFER_DESC vertexBufferDesc = {};
vertexBufferDesc.ByteWidth = sizeof(vertexData);
vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
vertexBufferDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; // using regular shader resource as vertex buffer for manual vertex fetch
vertexBufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
vertexBufferDesc.StructureByteStride = 5 * 4; // 5 floats per vertex (float3 position, float2 texcoord)
D3D11_SUBRESOURCE_DATA vertexBufferData = { vertexData };
ID3D11Buffer* vertexBuffer;
device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &vertexBuffer);
D3D11_SHADER_RESOURCE_VIEW_DESC vertexBufferSRVdesc = {};
vertexBufferSRVdesc.Format = DXGI_FORMAT_UNKNOWN;
vertexBufferSRVdesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
vertexBufferSRVdesc.Buffer.NumElements = sizeof(vertexData) / vertexBufferDesc.StructureByteStride;
ID3D11ShaderResourceView* vertexBufferSRV;
device->CreateShaderResourceView(vertexBuffer, &vertexBufferSRVdesc, &vertexBufferSRV);
///////////////////////////////////////////////////////////////////////////////////////////////
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);
///////////////////////////////////////////////////////////////////////////////////////////////
D3D11_RASTERIZER_DESC1 rasterizerDesc = {};
rasterizerDesc.FillMode = D3D11_FILL_SOLID;
rasterizerDesc.CullMode = D3D11_CULL_BACK;
ID3D11RasterizerState1* cullBackRS;
device->CreateRasterizerState1(&rasterizerDesc, &cullBackRS);
rasterizerDesc.CullMode = D3D11_CULL_FRONT;
ID3D11RasterizerState1* cullFrontRS;
device->CreateRasterizerState1(&rasterizerDesc, &cullFrontRS);
///////////////////////////////////////////////////////////////////////////////////////////////
ID3DBlob* framebufferVSBlob;
D3DCompileFromFile(L"shaders.hlsl", nullptr, nullptr, "framebuffer_vs", "vs_5_0", 0, 0, &framebufferVSBlob, nullptr);
ID3D11VertexShader* framebufferVS;
device->CreateVertexShader(framebufferVSBlob->GetBufferPointer(), framebufferVSBlob->GetBufferSize(), nullptr, &framebufferVS);
///////////////////////////////////////////////////////////////////////////////////////////////
ID3DBlob* framebufferPSBlob;
D3DCompileFromFile(L"shaders.hlsl", nullptr, nullptr, "framebuffer_ps", "ps_5_0", 0, 0, &framebufferPSBlob, nullptr);
ID3D11PixelShader* framebufferPS;
device->CreatePixelShader(framebufferPSBlob->GetBufferPointer(), framebufferPSBlob->GetBufferSize(), nullptr, &framebufferPS);
/////////////////////////////////////////////////////////////////////////////////////////////
ID3DBlob* shadowmapVSBlob;
D3DCompileFromFile(L"shaders.hlsl", nullptr, nullptr, "shadowmap_vs", "vs_5_0", 0, 0, &shadowmapVSBlob, nullptr);
ID3D11VertexShader* shadowmapVS;
device->CreateVertexShader(shadowmapVSBlob->GetBufferPointer(), shadowmapVSBlob->GetBufferSize(), nullptr, &shadowmapVS);
///////////////////////////////////////////////////////////////////////////////////////////////
FLOAT framebufferClear[4] = { 0.025f, 0.025f, 0.025f, 1 };
D3D11_VIEWPORT framebufferVP = { 0, 0, static_cast<float>(framebufferDepthDesc.Width), static_cast<float>(framebufferDepthDesc.Height), 0, 1 };
D3D11_VIEWPORT shadowmapVP = { 0, 0, static_cast<float>(shadowmapDepthDesc.Width), static_cast<float>(shadowmapDepthDesc.Height), 0, 1 };
ID3D11ShaderResourceView* nullSRV = nullptr; // null srv used for unbinding resources
///////////////////////////////////////////////////////////////////////////////////////////////
Constants constants = { 2.0f / (framebufferVP.Width / framebufferVP.Height), 0, 0, 0, 0, 2, 0, 0, 0, 0, 1.125f, 1, 0, 0, -1.125f, 0, // camera projection matrix (perspective)
0.5f, 0, 0, 0, 0, 0.5f, 0, 0, 0, 0, 0.125f, 0, 0, 0, -0.125f, 1 }; // light projection matrix (orthographic)
constants.LightRotation = { 0.8f, 0.6f, 0.0f };
constants.ModelRotation = { 0.0f, 0.0f, 0.0f };
constants.ModelTranslation = { 0.0f, 0.0f, 4.0f };
constants.ShadowmapSize = { shadowmapVP.Width, shadowmapVP.Height };
///////////////////////////////////////////////////////////////////////////////////////////////
while (true)
{
MSG msg;
while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_KEYDOWN) return 0;
DispatchMessageA(&msg);
}
///////////////////////////////////////////////////////////////////////////////////////////
constants.ModelRotation.x += 0.001f;
constants.ModelRotation.y += 0.005f;
constants.ModelRotation.z += 0.003f;
///////////////////////////////////////////////////////////////////////////////////////////
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->ClearDepthStencilView(shadowmapDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);
deviceContext->OMSetRenderTargets(0, nullptr, shadowmapDSV); // null rendertarget for depth only
deviceContext->OMSetDepthStencilState(depthStencilState, 0);
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); // using triangle strip this time
deviceContext->VSSetConstantBuffers(0, 1, &constantBuffer);
deviceContext->VSSetShaderResources(0, 1, &vertexBufferSRV);
deviceContext->VSSetShader(shadowmapVS, nullptr, 0);
deviceContext->RSSetViewports(1, &shadowmapVP);
deviceContext->RSSetState(cullFrontRS);
deviceContext->PSSetShader(nullptr, nullptr, 0); // null pixelshader for depth only
///////////////////////////////////////////////////////////////////////////////////////////
deviceContext->DrawInstanced(8, 24, 0, 0); // render shadowmap (light pov)
///////////////////////////////////////////////////////////////////////////////////////////
deviceContext->ClearRenderTargetView(framebufferRTV, framebufferClear);
deviceContext->ClearDepthStencilView(framebufferDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);
deviceContext->OMSetRenderTargets(1, &framebufferRTV, framebufferDSV);
deviceContext->VSSetShader(framebufferVS, nullptr, 0);
deviceContext->RSSetViewports(1, &framebufferVP);
deviceContext->RSSetState(cullBackRS);
deviceContext->PSSetShaderResources(1, 1, &shadowmapSRV);
deviceContext->PSSetShader(framebufferPS, nullptr, 0);
///////////////////////////////////////////////////////////////////////////////////////////
deviceContext->DrawInstanced(8, 24, 0, 0); // render framebuffer (camera pov)
///////////////////////////////////////////////////////////////////////////////////////////
deviceContext->PSSetShaderResources(1, 1, &nullSRV); // release shadowmap as srv to avoid srv/dsv conflict
///////////////////////////////////////////////////////////////////////////////////////////
swapChain->Present(1, 0);
}
}
cbuffer constants : register(b0)
{
row_major float4x4 cameraprojection;
row_major float4x4 lightprojection;
float3 lightrotation;
float3 modelrotation;
float4 modeltranslation;
float2 shadowmapsize;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
struct input
{
float3 position : POS;
float2 texcoord : TEX;
};
struct output
{
float4 position : SV_POSITION;
float4 lightpos : LPS;
float2 texcoord : TEX;
float4 color : COL;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
StructuredBuffer<input> vertexbuffer : register(t0);
Texture2D<float> shadowmap : register(t1);
///////////////////////////////////////////////////////////////////////////////////////////////////
float3 get_rotation(uint i)
{
return float3(max(0, float(i / 4) * 2 - 7), i / 4 % 5, i % 4) * 1.5708f; // generate XYZ rotation from instance id
}
float4x4 get_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 };
return mul(mul(z, y), x);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
output framebuffer_vs(uint vertexid : SV_VERTEXID, uint instanceid : SV_INSTANCEID)
{
input myinput = vertexbuffer[vertexid]; // manual vertex fetch
static float4 normal[2] = { { 0, 0, -1, 0 }, { 0, -1, 0, 0 } };
static float3 color [6] = { { 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 } };
float4x4 modeltransform = mul(get_rotation_matrix(get_rotation(instanceid)), get_rotation_matrix(modelrotation));
float4x4 lighttransform = mul(modeltransform, get_rotation_matrix(lightrotation));
float light = clamp(dot(mul(normal[vertexid / 4], lighttransform), normal[0]), 0.0f, 1.0f);
output myoutput;
myoutput.position = mul(mul(float4(myinput.position, 1.0f), modeltransform) + modeltranslation, cameraprojection);
myoutput.lightpos = mul(mul(float4(myinput.position, 1.0f), lighttransform) + modeltranslation, lightprojection);
myoutput.texcoord = myinput.texcoord;
myoutput.color = float4(color[instanceid / 4], light);
myoutput.lightpos.xy = (myoutput.lightpos.xy * float2(0.5f, -0.5f) + 0.5f) * shadowmapsize;
return myoutput;
}
float4 framebuffer_ps(output myinput) : SV_TARGET
{
float3 color = myinput.color.rgb * ((uint(myinput.texcoord.x * 2) ^ uint(myinput.texcoord.y * 2)) & 1 ? 0.25f : 1.0f);
float light = myinput.color.a;
if (light > 0.0f && shadowmap[myinput.lightpos.xy] < myinput.lightpos.z)
{
light *= 0.2;
}
return float4(color * (light * 0.8f + 0.2f), 1);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
float4 shadowmap_vs(uint vertexid : SV_VERTEXID, uint instanceid : SV_INSTANCEID) : SV_POSITION
{
float4x4 modeltransform = mul(get_rotation_matrix(get_rotation(instanceid)), get_rotation_matrix(modelrotation));
float4x4 lighttransform = mul(modeltransform, get_rotation_matrix(lightrotation));
return mul(mul(float4(vertexbuffer[vertexid].position, 1.0f), lighttransform) + modeltranslation, lightprojection);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment