-
Notifications
You must be signed in to change notification settings - Fork 511
Line drawing and anti aliasing
Here we learn how to render a grid in 3D, and enable multi-sample anti-aliasing (MSAA).
First create a new project using the instructions from the first two lessons: The basic game loop and Adding the DirectX Tool Kit which we will use for this lesson.
In the Game.h file, add the following variables to the bottom of the Game class's private declarations:
std::unique_ptr<DirectX::CommonStates> m_states;
std::unique_ptr<DirectX::BasicEffect> m_effect;
std::unique_ptr<DirectX::PrimitiveBatch<DirectX::VertexPositionColor>> m_batch;
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
DirectX::SimpleMath::Matrix m_world;
DirectX::SimpleMath::Matrix m_view;
DirectX::SimpleMath::Matrix m_proj;
In Game.cpp, add to the TODO of CreateDevice:
m_world = Matrix::Identity;
m_states = std::make_unique<CommonStates>(m_d3dDevice.Get());
m_effect = std::make_unique<BasicEffect>(m_d3dDevice.Get());
m_effect->SetVertexColorEnabled(true);
void const* shaderByteCode;
size_t byteCodeLength;
m_effect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);
DX::ThrowIfFailed(
m_d3dDevice->CreateInputLayout(VertexPositionColor::InputElements,
VertexPositionColor::InputElementCount,
shaderByteCode, byteCodeLength,
m_inputLayout.ReleaseAndGetAddressOf()));
m_batch = std::make_unique<PrimitiveBatch<VertexPositionColor>>(m_d3dContext.Get());
Optional: You can simplify the last part using DirectXHelpers:
m_world = Matrix::Identity;
m_states = std::make_unique<CommonStates>(m_d3dDevice.Get());
m_effect = std::make_unique<BasicEffect>(m_d3dDevice.Get());
m_effect->SetVertexColorEnabled(true);
DX::ThrowIfFailed(
CreateInputLayoutFromEffect<VertexPositionColor>(m_d3dDevice.Get(), m_effect.get(),
m_inputLayout.ReleaseAndGetAddressOf()
);
m_batch = std::make_unique<PrimitiveBatch<VertexPositionColor>>(m_d3dContext.Get());
In Game.cpp, add to the TODO of CreateResources:
m_view = Matrix::CreateLookAt(Vector3(2.f, 2.f, 2.f),
Vector3::Zero, Vector3::UnitY);
m_proj = Matrix::CreatePerspectiveFieldOfView(XM_PI / 4.f,
float(backBufferWidth) / float(backBufferHeight), 0.1f, 10.f);
m_effect->SetView(m_view);
m_effect->SetProjection(m_proj);
In Game.cpp, add to the TODO of OnDeviceLost:
m_states.reset();
m_effect.reset();
m_batch.reset();
m_inputLayout.Reset();
In Game.cpp, add to the TODO of Render:
m_d3dContext->OMSetBlendState( m_states->Opaque(), nullptr, 0xFFFFFFFF );
m_d3dContext->OMSetDepthStencilState( m_states->DepthNone(), 0 );
m_d3dContext->RSSetState( m_states->CullNone() );
m_effect->SetWorld(m_world);
m_effect->Apply(m_d3dContext.Get());
m_d3dContext->IASetInputLayout(m_inputLayout.Get());
m_batch->Begin();
Vector3 xaxis(2.f, 0.f, 0.f);
Vector3 yaxis(0.f, 0.f, 2.f);
Vector3 origin = Vector3::Zero;
size_t divisions = 20;
for( size_t i = 0; i <= divisions; ++i )
{
float fPercent = float(i) / float(divisions);
fPercent = ( fPercent * 2.0f ) - 1.0f;
Vector3 scale = xaxis * fPercent + origin;
VertexPositionColor v1( scale - yaxis, Colors::White );
VertexPositionColor v2( scale + yaxis, Colors::White );
m_batch->DrawLine( v1, v2 );
}
for( size_t i = 0; i <= divisions; i++ )
{
float fPercent = float(i) / float(divisions);
fPercent = ( fPercent * 2.0f ) - 1.0f;
Vector3 scale = yaxis * fPercent + origin;
VertexPositionColor v1( scale - xaxis, Colors::White );
VertexPositionColor v2( scale + xaxis, Colors::White );
m_batch->DrawLine( v1, v2 );
}
m_batch->End();
Build and run to see a 3D grid.
Taking a closer look at the grid in the previous screenshot, you can see the lines are a little thin and jagged in places. To make this more visible, in Game.cpp, add to the TODO of Update:
m_world = Matrix::CreateRotationY( cosf( static_cast<float>(timer.GetTotalSeconds())));
Build and run to see the grid spinning, and notice the slight shimmering of the lines--it will be more obvious if you maximize the window size.
There are two approaches to addressing this problem, known as aliasing.
The first is to enable a special anti-aliasing mode specific to line drawing in Direct3D.
In the Game.h file, add the following variable to the bottom of the Game class's private declarations:
Microsoft::WRL::ComPtr<ID3D11RasterizerState> m_raster;
In Game.cpp, add to the TODO of CreateDevice:
CD3D11_RASTERIZER_DESC rastDesc(D3D11_FILL_SOLID, D3D11_CULL_NONE, FALSE,
D3D11_DEFAULT_DEPTH_BIAS, D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, TRUE, FALSE, FALSE, TRUE);
DX::ThrowIfFailed(m_d3dDevice->CreateRasterizerState( &rastDesc,
m_raster.ReleaseAndGetAddressOf() ));
This creates a raster state that is the same as our standard
CullNone
but withAntialiasedLineEnable
set to TRUE andMultisampleEnable
set to FALSE.
In Game.cpp, add to the TODO of OnDeviceLost:
m_raster.Reset();
In Game.cpp, modify Render, changing:
m_d3dContext->RSSetState(m_states->CullNone());
to
m_d3dContext->RSSetState(m_raster.Get());
Build and run to see the shimmering of the lines lessen, although they will appear to be a bit thicker than a single pixel.
A second more general solution is to use Multisample anti-aliasing (MSAA) which uses more video memory and pixel-fill performance to achieve higher quality rendering results. In this case, we will make use of 4x MSAA where the render target and the depth buffer will be 4 times larger. MSAA can be used with all primitives, not just lines.
In Game.cpp, modify CreateDevice:
CD3D11_RASTERIZER_DESC rastDesc(D3D11_FILL_SOLID, D3D11_CULL_NONE, FALSE,
D3D11_DEFAULT_DEPTH_BIAS, D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, TRUE, FALSE, TRUE, FALSE);
This creates a raster state that is the same as our standard
CullNone
which hasMultisampleEnable
set to TRUE.
In Game.cpp, modify CreateResources:
Change the sample count in both places for DXGI_SWAP_CHAIN_DESC1
and DXGI_SWAP_CHAIN_DESC
swapChainDesc.SampleDesc.Count = 4;
Change the depth/stencil texture description to:
CD3D11_TEXTURE2D_DESC depthStencilDesc(depthBufferFormat,
backBufferWidth, backBufferHeight,
1, 1, D3D11_BIND_DEPTH_STENCIL, D3D11_USAGE_DEFAULT, 0, 4, 0);
Change the depth/stencil view description to:
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2DMS);
Build and run to see the shimmering of the lines lessen compared to the first version, and is slightly less thickened than when we used the AA line mode.
Troubleshooting: If you get an exception, it may be because your device does not support 4x MSAA--which you can confirm by checking the debug device output at the failure point. Direct3D Feature Level 10.1 or later devices all support 4x MSAA for most render target formats, but some 10.0 and 9.x feature levels do not. You can try using 2 instead of 4 in the sample counts if this happens. If you have a Feature Level 11.0 or later device, you can try using 4 or 8. Be sure to change
DXGI_SWAP_CHAIN_DESC1
,DXGI_SWAP_CHAIN_DESC
, anddepthStencilDesc
. See also CheckMultisampleQualityLevels.
The ability to create an MSAA DXGI swap chain is only supported for the older "bit-blt" style presentation modes, specifically DXGI_SWAP_EFFECT_DISCARD
or DXGI_SWAP_EFFECT_SEQUENTIAL
. The newer "flip" style presentation modes DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
or DXGI_SWAP_EFFECT_FLIP_DISCARD
required for Universal Windows Platform (UWP) apps don't support creating MSAA swap chains. Instead, you create your own MSAA render target and then explicitly resolve from MSAA to single-sample via ResolveSubresource
to the DXGI back-buffer for presentation. See Multisampling in Universal Windows Platform (UWP) apps and the SimpleMSAA sample which details how you implement MSAA in these cases.
-
PrimitiveBatch is ideally suited for drawing debug displays such as visualizing bounding volumes, collision data, etc. For more on this, see DebugDraw.
-
This tutorial uses the standard 3D graphics "world -> view -> projection" transformation pipeline. If you are unfamiliar with this pipeline and/or matrix transformations, see the math tutorial for additional learning resources.
Next lesson: 3D shapes
DirectX Tool Kit docs CommonStates, Effects, PrimitiveBatch, VertexTypes
FXAA
SMAA
Spatial anti-alising
All content and source code for this package are subject to the terms of the MIT License.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
- Universal Windows Platform apps
- Windows desktop apps
- Windows 11
- Windows 10
- Windows 8.1
- Windows 7 Service Pack 1
- Xbox One
- x86
- x64
- ARM64
- Visual Studio 2022
- Visual Studio 2019 (16.11)
- clang/LLVM v12 - v18
- MinGW 12.2, 13.2
- CMake 3.20