This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
1420
views
11. Render States
I want to cover the Render states before we start doing anything else, so here we will learn about the different render states and how to use them! The outcome of this lesson will be a nice little blue wireframe grid!
This lesson modifies the transformations lessons code. In Direct3D, things stay in their current state. Nothing goes back to it's "default" state. An example is our primitive topology. Once we set it, like we did for a triangle list, it will stay a triangle list until we explicitly change it. Like our vertex and index buffers. Once we bind them to the pipeline, they will stay there until we change them. In this lesson, we will cover "render states". What render states do is encapsulate settings that can be used to configure direct3d. These are the different states we can customize: **ID3D10RasterizerState **- This interface is used to customize the rasterizer stage of the pipeline. This is the one we will be using in this lesson. **ID3D10BlendState **- This one can be used for blending. We will not discuss blending yet, so we don't need to worry about this state quite yet. **ID3D10DepthStencilState **- This state interface is used to configure the depth and stencil tests. We will talk about the stencil buffer later, and the depth buffer is set to do the default depth tests, so we don't need to worry about this one yet either. The first line here is declaring a new ID3D10RasterizerState interface. This is what we will use to store our render state into, so we can set it later. The next couple lines are used for creating our grid. ID3D10RasterizerState* WireFrame; DWORD NumVertices; DWORD NumFaces; DWORD rows = 10; DWORD cols = 10; Now we go down to the initScene where we are creating our grid. We use std::vector because C++ will not let us compile an array that does not have a constant variable (NumVertices is not a constant). Other than that, if you have gotten this far in my lessons, you should be able to figure out what is going on here. NumVertices = rows * cols; NumFaces = (rows-1)*(cols-1)*2; std::vector vertices(NumVertices); for(DWORD i = 0; i < rows; ++i) { float z = ((rows - 1)* 0.5f) - i; for(DWORD j = 0; j < cols; ++j) { float x = ((cols - 1) * 0.5f) - j; vertices[i*cols+j].pos = D3DXVECTOR3(x, 0, z); vertices[i*cols+j].color = D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f); } } Next we change the bytewidth of our vertex buffer to store the number of vertices (NumVertices). bd.ByteWidth = sizeof( Vertex ) * NumVertices; After that we define our index buffer. std::vector indices(NumFaces * 3); int k = 0; for(DWORD i = 0; i < rows-1; ++i) { for(DWORD j = 0; j < cols-1; ++j) { indices[k] = i*cols+j; indices[k+1] = i*cols+j+1; indices[k+2] = (i+1)*cols+j; indices[k+3] = (i+1)*cols+j; indices[k+4] = i*cols+j+1; indices[k+5] = (i+1)*cols+j+1; k += 6; // next quad } } Then we set the bytewidth of our index buffer to be the correct size. ibd.ByteWidth = sizeof(DWORD) * NumFaces * 3; Here is where the heart of the lesson is. We start here by declaring a new D3D10_RASTERIZER_DESC called wfdesc. This will hold the defenition of our rasterizer state we want to create. This structure looks like this: typedef struct D3D10_RASTERIZER_DESC { D3D10_FILL_MODE FillMode; D3D10_CULL_MODE CullMode; BOOL FrontCounterClockwise; INT DepthBias; FLOAT DepthBiasClamp; FLOAT SlopeScaledDepthBias; BOOL DepthClipEnable; BOOL ScissorEnable; BOOL MultisampleEnable; BOOL AntialiasedLineEnable; } D3D10_RASTERIZER_DESC; FillMode - The two things we can put here are D3D10_FILL_WIREFRAME for wireframe rendering (which we will do in this lesson), or D3D10_FILL_SOLID for solid rendering, which we normally would do. CullMode - We can put D3D10_CULL_NONE to disable culling (which is not rendering the other side of a triangle), or D3D10_CULL_FRONT for front face culling (so the front face will not be rendered), or D3D10_CULL_BACK to cull the back face, which is default. FrontCounterClockwise - This is either set to true or false, true means that if the triangles vertices are rendered clockwise to the camera, then that is the front face. False is the opposite. DepthBias - Specifies the depth value added to a given pixel. The default value is 0. DepthBiasClamp - Specifies the maximum depth bias of a pixel. The default value is 0.0f. SlopeScaledDepthBias - Specifies a scalar on a given pixel's slope. The default value is 0.0f. DepthClipEnable - Enables or disables clipping based on distance. ScissorEnable - Enable or disables scissor-rectangle culling. All pixels outside an active scissor rectangle are culled. The default value is FALSE. MultisampleEnable - Enables or disables multisample antialiasing. AntialiasedLineEnable - Enable or disables line antialiasing. Note that this option only applies when alpha blending is enabled, you are drawing lines, and the MultisampleEnable member is FALSE. The default value is FALSE. D3D10_RASTERIZER_DESC wfdesc; We start by making sure the memory is cleared. Then we set the fillmode to D3D10_FILL_WIREFRAME so direct3d renders our grid in wireframe. Then we set cullmode to D3D10_CULL_BACK because we are only going to be looking at the front of the grid. This is actually the default for cullmode anyway, so we don't really need to specify it here. After that we set FrontCounterClockwise to false, which is the default also. Finally, we create our state with the CreateRasterizerState method of our device interface. The first parameter is a pointer to the description of our state we just filled out, the second is where our state will be stored. ZeroMemory(&wfdesc, sizeof(D3D10_RASTERIZER_DESC)); wfdesc.FillMode = D3D10_FILL_WIREFRAME; wfdesc.CullMode = D3D10_CULL_BACK; wfdesc.FrontCounterClockwise = false; hr = d3dDevice->CreateRasterizerState(&wfdesc, &WireFrame); if(FAILED(hr)) { MessageBox(0, L"RS STATE Creation - Failed", L"Error", MB_OK); return false; } Finally we set our state using the RSSetState method of our device interface. Usually we will set the state right before we draw the object that is using the state. But since we only have one object, all objects in our scene will use this state, so we can set it in the initScene function. The only parameter this one takes is the created state we wish to set to the rasterizer stage of the pipeline. d3dDevice->RSSetState(WireFrame); I hope this helps you understand states a little better in direct3d 10! Here's the final code: main.cpp #include <Windows.h> #include <d3d10.h> #include <d3dx10.h> #include <string> #include <vector> #pragma comment(lib, "D3D10.lib") #pragma comment(lib, "d3dx10d.lib") LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; const int Width = 800; const int Height = 600; bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); HRESULT hr; ID3D10Device* d3dDevice; IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RenderTargetView; ID3D10Effect* FX; ID3D10InputLayout* VertexLayout; ID3D10Buffer* VertexBuffer; ID3D10Buffer* IndexBuffer; ID3D10EffectTechnique* Technique; ID3D10DepthStencilView* DepthStencilView; ID3D10Texture2D* DepthStencilBuffer; ID3D10EffectMatrixVariable* fxWVPVar; D3DXMATRIX WVP; D3DXMATRIX World; D3DXMATRIX View; D3DXMATRIX Projection; D3DXVECTOR3 Position; D3DXVECTOR3 Target; D3DXVECTOR3 Up; ///////////////////////////////////new///////////////////////////////////////////// ID3D10RasterizerState* WireFrame; DWORD NumVertices; DWORD NumFaces; DWORD rows = 10; DWORD cols = 10; ///////////////////////////////////new///////////////////////////////////////////// bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitScene(); void DrawScene(); bool ReleaseObjects(); int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); struct Vertex { D3DXVECTOR3 pos; D3DXCOLOR color; }; int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3dApp(hInstance)) { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); if(!ReleaseObjects()) { MessageBox(0, L"Object Releasing - Failed", L"Error", MB_OK); return 0; } return 0; } bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Window Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3dApp(HINSTANCE hInstance) { UINT createDeviceFlags = 0; D3D10_DRIVER_TYPE driverTypes[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] ); DXGI_SWAP_CHAIN_DESC scd; scd.BufferDesc.Width = Width; scd.BufferDesc.Height = Height; scd.BufferDesc.RefreshRate.Numerator = 60; scd.BufferDesc.RefreshRate.Denominator = 1; scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //no multisampling scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.BufferCount = 1; scd.OutputWindow = hwnd; scd.Windowed = true; scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; scd.Flags = 0; D3D10CreateDeviceAndSwapChain(0, D3D10_DRIVER_TYPE_HARDWARE, 0, 0, D3D10_SDK_VERSION, &scd, &SwapChain, &d3dDevice); ID3D10Texture2D* backBuffer; SwapChain->GetBuffer(0, _uuidof(ID3D10Texture2D), reinterpret_cast<void**>(&backBuffer)); d3dDevice->CreateRenderTargetView(backBuffer, 0, &RenderTargetView); backBuffer->Release(); D3D10_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D10_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; d3dDevice->CreateTexture2D(&depthStencilDesc, NULL, &DepthStencilBuffer); d3dDevice->CreateDepthStencilView(DepthStencilBuffer, NULL, &DepthStencilView); d3dDevice->OMSetRenderTargets(1, &RenderTargetView, DepthStencilView); // Setup the viewport D3D10_VIEWPORT vp; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; d3dDevice->RSSetViewports( 1, &vp ); D3DXMatrixIdentity( &World ); ////////////////////new////////////////////////////////////////////////////////////////////// Position = D3DXVECTOR3( 0.0f, 10.0f, -12.0f ); ////////////////////new////////////////////////////////////////////////////////////////////// Target = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); Up = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); D3DXMatrixLookAtLH( &View, &Position, &Target, &Up ); return true; } bool InitScene() { ///////////////////////////////////new///////////////////////////////////////////// NumVertices = rows * cols; NumFaces = (rows-1)*(cols-1)*2; std::vector<Vertex> vertices(NumVertices); for(DWORD i = 0; i < rows; ++i) { float z = ((rows - 1)* 0.5f) - i; for(DWORD j = 0; j < cols; ++j) { float x = ((cols - 1) * 0.5f) - j; vertices[i*cols+j].pos = D3DXVECTOR3(x, 0, z); vertices[i*cols+j].color = D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f); } } ///////////////////////////////////new///////////////////////////////////////////// D3D10_BUFFER_DESC bd; bd.Usage = D3D10_USAGE_IMMUTABLE; ///////////////////////////////////new///////////////////////////////////////////// bd.ByteWidth = sizeof( Vertex ) * NumVertices; ///////////////////////////////////new///////////////////////////////////////////// bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; bd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA InitData; InitData.pSysMem = &vertices[0]; d3dDevice->CreateBuffer( &bd, &InitData, &VertexBuffer ); ///////////////////////////////////new///////////////////////////////////////////// std::vector<DWORD> indices(NumFaces * 3); int k = 0; for(DWORD i = 0; i < rows-1; ++i) { for(DWORD j = 0; j < cols-1; ++j) { indices[k] = i*cols+j; indices[k+1] = i*cols+j+1; indices[k+2] = (i+1)*cols+j; indices[k+3] = (i+1)*cols+j; indices[k+4] = i*cols+j+1; indices[k+5] = (i+1)*cols+j+1; k += 6; // next quad } } ///////////////////////////////////new///////////////////////////////////////////// D3D10_BUFFER_DESC ibd; ibd.Usage = D3D10_USAGE_IMMUTABLE; ///////////////////////////////////new///////////////////////////////////////////// ibd.ByteWidth = sizeof(DWORD) * NumFaces * 3; ///////////////////////////////////new///////////////////////////////////////////// ibd.BindFlags = D3D10_BIND_INDEX_BUFFER; ibd.CPUAccessFlags = 0; ibd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = &indices[0]; d3dDevice->CreateBuffer(&ibd, &iinitData, &IndexBuffer); UINT stride = sizeof( Vertex ); UINT offset = 0; d3dDevice->IASetVertexBuffers( 0, 1, &VertexBuffer, &stride, &offset ); d3dDevice->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0); D3D10_INPUT_ELEMENT_DESC layout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0} }; ID3D10Blob* compilationErrors = 0; HRESULT hr = 0; hr = D3DX10CreateEffectFromFile( L"vertex.fx", NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, d3dDevice, NULL, NULL, &FX, &compilationErrors, NULL ); if(FAILED(hr)) { MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0); compilationErrors->Release(); return false; } Technique = FX->GetTechniqueByName( "Tech" ); fxWVPVar = FX->GetVariableByName("WVP")->AsMatrix(); D3D10_PASS_DESC PassDesc; Technique->GetPassByIndex( 0 )->GetDesc( &PassDesc ); d3dDevice->CreateInputLayout( layout, 2, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &VertexLayout ); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); ///////////////////////////////////new///////////////////////////////////////////// D3D10_RASTERIZER_DESC wfdesc; ZeroMemory(&wfdesc, sizeof(D3D10_RASTERIZER_DESC)); wfdesc.FillMode = D3D10_FILL_WIREFRAME; wfdesc.CullMode = D3D10_CULL_FRONT; wfdesc.FrontCounterClockwise = false; hr = d3dDevice->CreateRasterizerState(&wfdesc, &WireFrame); if(FAILED(hr)) { MessageBox(0, L"RS STATE Creation - Failed", L"Error", MB_OK); return false; } d3dDevice->RSSetState(WireFrame); ///////////////////////////////////new///////////////////////////////////////////// return true; } bool ReleaseObjects() { if( d3dDevice ) d3dDevice->ClearState(); if( VertexBuffer ) VertexBuffer->Release(); if( IndexBuffer ) IndexBuffer->Release(); if( VertexLayout ) VertexLayout->Release(); if( FX ) FX->Release(); if( RenderTargetView ) RenderTargetView->Release(); if( SwapChain ) SwapChain->Release(); if( d3dDevice ) d3dDevice->Release(); return true; } void DrawScene() { //Draw Scene Here D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f); d3dDevice->ClearRenderTargetView( RenderTargetView, bgColor ); d3dDevice->ClearDepthStencilView(DepthStencilView, D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, 1.0f, 0); D3DXMatrixPerspectiveFovLH(&Projection, 0.4f*3.14f, Width/Height, 1.0f, 1000.0f); WVP = World * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); D3D10_TECHNIQUE_DESC techDesc; Technique->GetDesc( &techDesc ); for( UINT p = 0; p < techDesc.Passes; ++p ) { Technique->GetPassByIndex( p )->Apply( 0 ); d3dDevice->DrawIndexed(NumFaces * 3, 0, 0); } SwapChain->Present( 0, 0 ); } int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ if(MessageBox(0, L"Are you sure you want to exit?", L"Really?", MB_YESNO | MB_ICONQUESTION) == IDYES) DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } vertex.fx cbuffer cbPerObject { float4x4 WVP; }; struct VS_OUTPUT //output structure for vertex shader { float4 Pos : SV_POSITION; float4 Color : COLOR0; }; // Vertex Shader VS_OUTPUT VS(float4 inPos : POSITION, float4 inColor : COLOR) { VS_OUTPUT output = (VS_OUTPUT)0; output.Pos = mul(inPos, WVP); output.Color = inColor; return output; //send color and position to pixel shader } // Pixel Shader float4 PS(VS_OUTPUT input) : SV_Target { return input.Color; // Set the color of the pixel to what we defined for the vertex. } technique10 Tech { pass P0 { SetVertexShader( CompileShader( vs_4_0, VS() ) ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } }
Sign in to comment