diff options
Diffstat (limited to 'directx9.html.markdown')
| -rw-r--r-- | directx9.html.markdown | 827 | 
1 files changed, 827 insertions, 0 deletions
| diff --git a/directx9.html.markdown b/directx9.html.markdown new file mode 100644 index 00000000..b51f418d --- /dev/null +++ b/directx9.html.markdown @@ -0,0 +1,827 @@ +--- +category: tool +tool: DirectX 9 +filename: learndirectx9.cpp +contributors: +    - ["Simon Deitermann", "s.f.deitermann@t-online.de"] +--- + +**Microsoft DirectX** is a collection of application programming interfaces (APIs) for handling tasks related to +multimedia, especially game programming and video, on Microsoft platforms. Originally, the names of these APIs +all began with Direct, such as Direct3D, DirectDraw, DirectMusic, DirectPlay, DirectSound, and so forth. [...] +Direct3D (the 3D graphics API within DirectX) is widely used in the development of video games for Microsoft +Windows and the Xbox line of consoles.<sup>[1]</sup> + +In this tutorial we will be focusing on DirectX 9, which is not as low-level as it's sucessors, which are aimed at programmers very familiar with how graphics hardware works. It makes a great starting point for learning Direct3D. In this tutorial I will be using the Win32-API for window handling and the DirectX 2010 SDK. + +## Window creation + +```cpp +#include <Windows.h> + +bool _running{ false }; + +LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { +    // Handle incoming message. +    switch (msg) { +        // Set running to false if the user tries to close the window. +        case WM_DESTROY: +            _running = false; +            PostQuitMessage(0); +            break; +    } +    // Return the handled event. +    return DefWindowProc(hWnd, msg, wParam, lParam); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, +                   LPSTR lpCmdLine, int nCmdShow) { +    // Set window properties we want to use. +    WNDCLASSEX wndEx{ }; +    wndEx.cbSize        = sizeof(WNDCLASSEX);        // structure size +    wndEx.style         = CS_VREDRAW | CS_HREDRAW;   // class styles +    wndEx.lpfnWndProc   = WndProc;                   // window procedure +    wndEx.cbClsExtra    = 0;                         // extra memory (struct) +    wndEx.cbWndExtra    = 0;                         // extra memory (window) +    wndEx.hInstance     = hInstance;                 // module instance +    wndEx.hIcon         = LoadIcon(nullptr, IDI_APPLICATION); // icon +    wndEx.hCursor       = LoadCursor(nullptr, IDC_ARROW);     // cursor +    wndEx.hbrBackground = (HBRUSH) COLOR_WINDOW;     // background color +    wndEx.lpszMenuName  = nullptr;                   // menu name +    wndEx.lpszClassName = "DirectXClass";            // register class name +    wndEx.hIconSm       = nullptr;                   // small icon (taskbar) +    // Register created class for window creation. +    RegisterClassEx(&wndEx); +    // Create a new window handle. +    HWND hWnd{ nullptr }; +    // Create a new window handle using the registered class. +    hWnd = CreateWindow("DirectXClass",      // registered class +                        "directx window",    // window title +                        WS_OVERLAPPEDWINDOW, // window style +                        50, 50,              // x, y (position) +                        1024, 768,           // width, height (size) +                        nullptr,             // parent window +                        nullptr,             // menu +                        hInstance,           // module instance +                        nullptr);            // struct for infos +    // Check if a window handle has been created. +    if (!hWnd) +        return -1;    +    // Show and update the new window. +    ShowWindow(hWnd, nCmdShow); +    UpdateWindow(hWnd); +    // Start the game loop and send incoming messages to the window procedure. +    _running = true; +    MSG msg{ }; +    while (_running) { +        while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) { +            TranslateMessage(&msg); +            DispatchMessage(&msg); +        } +    }   +    return 0; +} +``` + +This should create a window, that can the moved, resized and closed. + +## Direct3D initialization + +```cpp +// Includes DirectX 9 structures and functions. +// Remember to link "d3d9.lib" and "d3dx9.lib". +// For "d3dx9.lib" the DirectX SDK (June 2010) is needed. +// Don't forget to set your subsystem to Windows. +#include <d3d9.h> +#include <d3dx9.h> +// Includes the ComPtr, a smart pointer automatically releasing COM objects. +#include <wrl.h> +using namespace Microsoft::WRL; +// Next we define some Direct3D9 interface structs we need. +ComPtr<IDirect3D9> _d3d{ }; +ComPtr<IDirect3DDevice9> _device{ }; +``` + +With all interfaces declared we can now initialize Direct3D. + +```cpp +bool InitD3D(HWND hWnd) { +    // Store the size of the window rectangle. +    RECT clientRect{ }; +    GetClientRect(hWnd, &clientRect); +    // Initialize Direct3D +    _d3d = Direct3DCreate9(D3D_SDK_VERSION); +    // Get the display mode which format will be the window format. +    D3DDISPLAYMODE displayMode{ }; +    _d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, // use default graphics card +                                &displayMode);      // display mode pointer +    // Next we have to set some presentation parameters. +    D3DPRESENT_PARAMETERS pp{ }; +    pp.BackBufferWidth = clientRect.right;    // width is window width +    pp.BackBufferHeight = clientRect.bottom;  // height is window height +    pp.BackBufferFormat = displayMode.Format; // use adapter format +    pp.BackBufferCount = 1;                   // 1 back buffer (default) +    pp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard after presentation +    pp.hDeviceWindow = hWnd;                  // associated window handle +    pp.Windowed = true;                       // display in window mode +    pp.Flags = 0;                             // no special flags +    // Variable to store results of methods to check if everything succeded. +    HRESULT result{ }; +    result = _d3d->CreateDevice(D3DADAPTER_DEFAULT, // use default graphics card +                                D3DDEVTYPE_HAL,     // use hardware acceleration +                                hWnd,               // the window handle +                                D3DCREATE_HARDWARE_VERTEXPROCESSING, +                                    // vertices are processed by the hardware +                                &pp,       // the present parameters +                                &_device); // struct to store the device +    // Return false if the device creation failed. +    // It is helpful to set breakpoints at the return line. +    if (FAILED(result)) +        return false; +    // Create a viewport which hold information about which region to draw to. +    D3DVIEWPORT9 viewport{ }; +    viewport.X = 0;         // start at top left corner +    viewport.Y = 0;         // .. +    viewport.Width = clientRect.right;   // use the entire window +    viewport.Height = clientRect.bottom; // .. +    viewport.MinZ = 0.0f;   // minimun view distance +    viewport.MaxZ = 100.0f; // maximum view distance +    // Apply the created viewport. +    result = _device->SetViewport(&viewport); +    // Always check if something failed. +    if (FAILED(result)) +        return false; +    // Everything was successful, return true. +    return true; +} +// ... +// Back in our WinMain function we call our initialization function. +// ... +// Check if Direct3D initialization succeded, else exit the application. +if (!InitD3D(hWnd)) +    return -1; +         +MSG msg{ }; +while (_running) { +    while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) { +        TranslateMessage(&msg); +        DispatchMessage(&msg); +    } +    // Clear to render target to a specified color. +    _device->Clear(0,               // number of rects to clear +                   nullptr,         // indicates to clear the entire window +                   D3DCLEAR_TARGET, // clear all render targets +                   D3DXCOLOR{ 1.0f, 0.0f, 0.0f, 1.0f }, // color (red) +                   0.0f,            // depth buffer clear value +                   0);              // stencil buffer clear value +    // ... +    // Drawing operations go here. +    // ... +    // Flip the front- and backbuffer. +    _device->Present(nullptr,  // no source rectangle +                     nullptr,  // no destination rectangle +                     nullptr,  // don't change the current window handle +                     nullptr); // pretty much always nullptr +} +// ... +``` + +Now the window should be displayed in a bright red color. + +## Vertex Buffer + +Let's create a vertex buffer to store the vertices for our triangle + +```cpp +// At the top of the file we need to add a include. +#include <vector> +// First we declare a new ComPtr holding a vertex buffer. +ComPtr<IDirect3DVertexBuffer9> _vertexBuffer{ }; +// Lets define a funtion to calculate the byte size of a std::vector +template <typename T> +unsigned int GetByteSize(const std::vector<T>& vec) { +    return sizeof(vec[0]) * vec.size(); +} +// Define "flexible vertex format" describing the content of our vertex struct. +// Use the defined color as diffuse color. +const unsigned long VertexStructFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE; +// Define a struct representing the vertex data the buffer will hold. +struct VStruct { +    float x, y, z;   // store the 3D position +    D3DCOLOR color;  // store a color +}; +// Declare a new function to create a vertex buffer. +IDirect3DVertexBuffer9* CreateBuffer(const std::vector<VStruct>& vertices) { +    // Declare the buffer to be returned. +    IDirect3DVertexBuffer9* buffer{ }; +    HRESULT result{ }; +    result = _device->CreateVertexBuffer( +                 GetByteSize(vertices), // vector size in bytes +                 0,                     // data usage +                 VertexStructFVF,       // FVF of the struct +                 D3DPOOL_DEFAULT,       // use default pool for the buffer +                 &buffer,               // receiving buffer +                 nullptr);              // special shared handle +    // Check if buffer was created successfully. +    if (FAILED(result)) +        return nullptr; +    // Create a data pointer for copying the vertex data +    void* data{ }; +    // Lock the buffer to get a buffer for data storage. +    result = buffer->Lock(0,                     // byte offset +                          GetByteSize(vertices), // size to lock +                          &data,                 // receiving data pointer +                          0);                    // special lock flags +    // Check if buffer was locked successfully. +    if (FAILED(result)) +        return nullptr; +    // Copy the vertex data using C standard libraries memcpy. +    memcpy(data, vertices.data(), GetByteSize(vertices)); +    buffer->Unlock(); +    // Set the FVF Direct3D uses for rendering. +    _device->SetFVF(VertexStructFVF); +    // If everything was successful return the filled vertex buffer. +    return buffer; +} +``` + +In our **WinMain** we can now call the new function after the Direct3D initialization. + +```cpp +// ... +if (!InitD3D(hWnd)) +    return -1; +// Define the vertices we need to draw a triangle. +// Values are declared in a clockwise direction else Direct3D would cull them. +// If you want to diable culling just call: +// _device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); +std::vector<VStruct> vertices { +    // Bottom left +    VStruct{ -1.0f, -1.0f, 1.0f, D3DXCOLOR{ 1.0f, 0.0f, 0.0f, 1.0f } }, +    // Top left +    VStruct{ -1.0f,  1.0f, 1.0f, D3DXCOLOR{ 0.0f, 1.0f, 0.0f, 1.0f } }, +    // Top right +    VStruct{  1.0f,  1.0f, 1.0f, D3DXCOLOR{ 0.0f, 0.0f, 1.0f, 1.0f } } +}; +// Try to create the vertex buffer else exit the application. +if (!(_vertexBuffer = CreateBuffer(vertices))) +    return -1; +// ... +``` + +## Transformations + +Before we can use the vertex buffer to draw our primitives, we first need to set up the matrices. + +```cpp +// Lets create a new funtions for the matrix transformations. +bool SetupTransform() { +    // Create a view matrix that transforms world space to +    // view space. +    D3DXMATRIX view{ }; +    // Use a left-handed coordinate system. +    D3DXMatrixLookAtLH( +        &view,                              // receiving matrix +        &D3DXVECTOR3{ 0.0f, 0.0f, -20.0f }, // "camera" position +        &D3DXVECTOR3{ 0.0f, 0.0f, 0.0f },   // position where to look at +        &D3DXVECTOR3{ 0.0f, 1.0f, 0.0f });  // positive y-axis is up +    HRESULT result{ }; +    result = _device->SetTransform(D3DTS_VIEW, &view); // apply the view matrix +    if (FAILED(result)) +        return false; +    // Create a projection matrix that defines the view frustrum. +    // It transforms the view space to projection space. +    D3DXMATRIX projection{ }; +    // Create a perspective projection using a left-handed coordinate system. +    D3DXMatrixPerspectiveFovLH( +        &projection,         // receiving matrix +        D3DXToRadian(60.0f), // field of view in radians +        1024.0f / 768.0f,    // aspect ratio (width / height) +        0.0f,                // minimum view distance +        100.0f);             // maximum view distance +    result = _device->SetTransform(D3DTS_PROJECTION, &projection); +    if (FAILED(result)) +        return false; +    // Disable lighting for now so we can see what we want to render. +    result = _device->SetRenderState(D3DRS_LIGHTING, false); +    // View and projection matrix are successfully applied, return true. +    return true; +} +// ... +// Back in the WinMain function we can now call the transformation function. +// ... +if (!(_vertexBuffer = CreateVertexBuffer(vertices))) +    return -1; +// Call the transformation setup function. +if (!SetupTransform()) +    return -1; +// ... +``` + +## Rendering + +Now that everything is setup we can start drawing our first 2D triangle in 3D space. + +```cpp +// ... +if (!SetupTransform()) +    return -1; +// First we have to bind our vertex buffer to the data stream. +HRESULT result{ }; +result = _device->SetStreamSource(0,                   // use the default stream +                                  _vertexBuffer.Get(), // pass the vertex buffer +                                  0,                   // no offset +                                  sizeof(VStruct));    // size of vertex struct +if (FAILED(result)) +    return -1; + +// Create a world transformation matrix and set it to an identity matrix. +D3DXMATRIX world{ }; +D3DXMatrixIdentity(&world); +// Create a scalation matrix scaling our primitve by 10 in the x, +// 10 in the y and keeping the z direction. +D3DXMATRIX scaling{ }; +D3DXMatrixScaling(&scaling, // matrix to scale +                  10,       // x scaling +                  10,       // y scaling +                  1);       // z scaling +// Create a rotation matrix storing the current rotation of our primitive. +// We set the current rotation matrix to an identity matrix for now. +D3DXMATRIX rotation{ }; +D3DXMatrixIdentity(&rotation); +// Now we multiply the scalation and rotation matrix and store the result +// in the world matrix. +D3DXMatrixMultiply(&world,     // destination matrix +                   &scaling,   // matrix 1 +                   &rotation); // matrix 2 +// Apply the current world matrix. +_device->SetTransform(D3DTS_WORLD, &world); +// Disable culling so we can see the back of our primitive when it rotates. +_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); +// The default cullmode is D3DCULL_CW. +// After we used our the rotation matrix for multiplication we can set it +// to rotate a small amount. +// D3DXToRadian() function converts degree to radians. +D3DXMatrixRotationY(&rotation,           // matrix to rotate +                    D3DXToRadian(0.5f)); // rotation angle in radians + +MSG msg{ }; +    while (_running) { +    // ... +        _device->Clear(0, nullptr, D3DCLEAR_TARGET, +                       D3DXCOLOR{ 0.0f, 0.0f, 0.0f, 1.0f }, 0.0f, 0); +        // With everything setup we can call the draw function. +        _device->BeginScene(); +        _device->DrawPrimitive(D3DPT_TRIANGLELIST, // primitive type +                               0,                  // start vertex +                               1);                 // primitive count +        _device->EndScene(); +     +        _device->Present(nullptr, nullptr, nullptr, nullptr); +        // We can keep multiplying the world matrix with our rotation matrix +        // to add it's rotation to the world matrix. +        D3DXMatrixMultiply(&world, &world, &rotation); +        // Update the modified world matrix. +        _device->SetTransform(D3DTS_WORLD, &world); +    // ... +``` + +You should now be viewing a 10x10 units colored triangle from 20 units away, rotating around its origin.<br> +You can find the complete working code here: [DirectX - 1](https://pastebin.com/YkSF2rkk) + +## Indexing + +To make it easier to draw primitives sharing a lot of vertices we can use indexing, so we only have to declare the unique vertices and put the order they are called in another array. + +```cpp +// First we declare a new ComPtr for our index buffer. +ComPtr<IDirect3DIndexBuffer9> _indexBuffer{ }; +// ... +// Declare a function creating a index buffer from a std::vector +IDirect3DIndexBuffer9* CreateIBuffer(std::vector<unsigned int>& indices) { +    IDirect3DIndexBuffer9* buffer{ }; +    HRESULT result{ }; +    result = _device->CreateIndexBuffer( +                 GetByteSize(indices), // vector size in bytes +                 0,                    // data usage  +                 D3DFMT_INDEX32,       // format is 32 bit int +                 D3DPOOL_DEFAULT,      // default pool +                 &buffer,              // receiving buffer +                 nullptr);             // special shared handle +    if (FAILED(result)) +        return nullptr; +    // Create a data pointer pointing to the buffer data. +    void* data{ }; +    result = buffer->Lock(0,                    // byte offset +                          GetByteSize(indices), // byte size +                          &data,                // receiving data pointer +                          0);                   // special lock flag +    if (FAILED(result)) +        return nullptr; +    // Copy the index data and unlock after copying. +    memcpy(data, indices.data(), GetByteSize(indices)); +    buffer->Unlock(); +    // Return the filled index buffer. +    return buffer; +} +// ... +// In our WinMain we can now change the vertex data and create new index data. +// ... +std::vector<VStruct> vertices { +    VStruct{ -1.0f, -1.0f, 1.0f, D3DXCOLOR{ 1.0f, 0.0f, 0.0f, 1.0f } }, +    VStruct{ -1.0f,  1.0f, 1.0f, D3DXCOLOR{ 0.0f, 1.0f, 0.0f, 1.0f } }, +    VStruct{  1.0f,  1.0f, 1.0f, D3DXCOLOR{ 0.0f, 0.0f, 1.0f, 1.0f } }, +    // Add a vertex for the bottom right. +    VStruct{  1.0f, -1.0f, 1.0f, D3DXCOLOR{ 1.0f, 1.0f, 0.0f, 1.0f } } +}; +// Declare the index data, here we build a rectangle from two triangles. +std::vector<unsigned int> indices { +    0, 1, 2, // the first triangle (b,left -> t,left -> t,right) +    0, 2, 3  // the second triangle (b,left -> t,right -> b,right) +}; +// ... +// Now we call the "CreateIBuffer" function to create a index buffer. +// ... +if (!(_indexBuffer = CreateIBuffer(indices))) +    return -1; +// ... +// After binding the vertex buffer we have to bind the index buffer to +// use indexed rendering. +result = _device->SetStreamSource(0, _vertexBuffer.Get(), 0, sizeof(VStruct)); +if (FAILED(result)) +    return -1; +// Bind the index data to the default data stream. +result = _device->SetIndices(_indexBuffer.Get()) +if (FAILED(result)) +    return -1; +// ... +// Now we replace the "DrawPrimitive" function with an indexed version. +_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, // primitive type +                              0,                  // base vertex index +                              0,                  // minimum index +                              indices.size(),     // amount of vertices +                              0,                  // start in index buffer +                              2);                 // primitive count +// ... +``` + +Now you should see a colored rectangle made up of 2 triangles. If you set the primitive count in the "DrawIndexedPrimitive" method to 1 only the first triangle should be rendered and if you set the start of the index buffer to 3 and the primitive count to 1 only the second triangle should be rendered.<br> +You can find the complete working code here: [DirectX - 2](https://pastebin.com/yWBPWPRG) + +## Vertex declaration + +Instead of using the old "flexible vertex format" we should use vertex declarations instead, as the FVF declarations get converted to vertex declarations internally anyway. + +```cpp +// First we have to REMOVE the following lines: +const unsigned long VertexStructFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE; +// and +_device->SetFVF(VertexStructFVF); +// ... +// We also have to change the vertex buffer creation FVF-flag. +result = _device->CreateVertexBuffer( +                      GetByteSize(vertices), +                      0, +                      0,        // <- 0 indicates we use vertex declarations +                      D3DPOOL_DEFAULT, +                      &buffer, +                      nullptr);  +// Next we have to declare a new ComPtr. +ComPtr<IDirect3DVertexDeclaration9> _vertexDecl{ }; +// ... +result = _device->SetIndices(_indexBuffer.Get()); +if (FAILED(result)) +    return -1; +// Now we have to declare and apply the vertex declaration. +// Create a vector of vertex elements making up the vertex declaration. +std::vector<D3DVERTEXELEMENT9> vertexDeclDesc { +    { 0,                     // stream index +      0,                     // byte offset from the struct beginning +      D3DDECLTYPE_FLOAT3,    // data type (3d float vector) +      D3DDECLMETHOD_DEFAULT, // tessellator operation +      D3DDECLUSAGE_POSTION,  // usage of the data +      0 },                   // index (multiples usage of the same type) +    { 0, +      12,                    // byte offset (3 * sizeof(float) bytes) +      D3DDECLTYPE_D3DCOLOR, +      D3DDECLMETHOD_DEFAULT, +      D3DDECLUSAGE_COLOR, +      0 }, +    D3DDECL_END()            // marks the end of the vertex declaration +}; +// After having defined the vector we can create a vertex declaration from it. +result = _device->CreateVertexDeclaration( +                      vertexDeclDesc.data(), // the vertex element array +                      &_vertexDecl);         // receiving pointer +if (FAILED(result))  +    return -1; +// Apply the created vertex declaration. +_device->SetVertexDeclaration(_vertexDecl.Get()); +// ... +``` + +## Shader + +The maximum shader model for Direct3D 9 is shader model 3.0. Even though every modern graphics card should support it, it is best to check for capabilities. + +```cpp +// ... +_device->SetVertexDeclaration(_vertexDecl.Get()); +// First we have to request the device capabilities. +D3DCAPS9 deviceCaps{ }; +_device->GetDeviceCaps(&deviceCaps); +// Now we check if shader model 3.0 is supported for the vertex shader. +if (deviceCaps.VertexShaderVersion < D3DVS_VERSION(3, 0)) +    return -1; +// And the same for the pixel shader. +if (deviceCaps.PixelShaderVersion < D3DPS_VERSION(3, 0)) +    return -1; +``` + +Now that we are sure shader model 3.0 is supported let's create the vertex and pixel shader files. +DirectX 9 introduced the HLSL (**High Level Shading Language**), a C-like shader language, which +simplified the shader programming a lot, as you could only write shaders in shader assembly in DirectX 8. +Let's create a simple vertex- and pixel shader.  + +**Vertex Shader** + +```cpp +// 3 4x4 float matrices representing the matrices we set in the fixed-function +// pipeline by using the SetTransform() method. +float4x4 projectionMatrix; +float4x4 viewMatrix; +float4x4 worldMatrix; +// The input struct to the vertex shader. +// It holds a 3d float vector for the position and a 4d float vector +// for the color. +struct VS_INPUT { +    float3 position : POSITION; +    float4 color : COLOR; +}; +// The output struct of the vertex shader, that is passed to the pixel shader. +struct VS_OUTPUT { +    float4 position : POSITION; +    float4 color : COLOR; +}; +// The main function of the vertex shader returns the output it sends to the +// pixel shader and receives it's input as a parameter. +VS_OUTPUT main(VS_INPUT input) { +    // Declare a empty struct, that the vertex shader returns. +    VS_OUTPUT output; +    // Set the output position to the input position and set +    // the w-component to 1, as the input position is a 3d vector and +    // the output position a 4d vector. +    output.position = float4(input.position, 1.0f); +    // Multiply the output position step by step with the world, view and +    // projection matrices. +    output.position = mul(output.position, worldMatrix);	 +    output.position = mul(output.position, viewMatrix); +    output.position = mul(output.position, projectionMatrix); +	// Pass the input color unchanged to the pixel shader. +    output.color = input.color; +    // Return the output struct to the pixel shader. +    // The position value is automatically used as the vertex position. +    return output; +} +``` + +**Pixel Shader** + +```cpp +// The pixel shader input struct must be the same as the vertex shader output! +struct PS_INPUT { +    float4 position : POSITION; +    float4 color : COLOR; +}; +// The pixel shader simply returns a 4d vector representing the vertex color. +// It receives it's input as a parameter just like the vertex shader. +// We have to declare the output semantic as color to it gets interpreted +// correctly. +float4 main(PS_INPUT input) : COLOR { +    return input.color; +} +``` + +For more on semantics: [DirectX - Semantics](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#vertex-shader-semantics) + +Now we have to do quite some changes to the code. + +```cpp +ComPtr<IDirect3DDevice9> _device{ }; +ComPtr<IDirect3DVertexBuffer9> _vertexBuffer{ }; +ComPtr<IDirect3DIndexBuffer9> _indexBuffer{ }; +ComPtr<IDirect3DVertexDeclaration9> _vertexDecl{ }; +// We have to add a ComPtr for the vertex- and pixel shader, aswell as one +// for the constants (matrices) in our vertex shader. +ComPtr<IDirect3DVertexShader9> _vertexShader{ }; +ComPtr<IDirect3DPixelShader9> _pixelShader{ }; +ComPtr<ID3DXConstantTable> _vertexTable{ }; +// Declare the world and rotation matrix as global, because we use them in +// WinMain and SetupTransform now. +D3DXMATRIX _worldMatrix{ }; +D3DXMATRIX _rotationMatrix{ }; +// ... +bool SetupTransform() { +    // Set the world and rotation matrix to an identity matrix. +    D3DXMatrixIdentity(&_worldMatrix); +    D3DXMatrixIdentity(&_rotationMatrix); +	 +    D3DXMATRIX scaling{ }; +    D3DXMatrixScaling(&scaling, 10, 10, 1); +    D3DXMatrixMultiply(&_worldMatrix, &scaling, &_rotationMatrix); +    // After multiplying the scalation and rotation matrix the have to pass +    // them to the shader, by using a method from the constant table +    // of the vertex shader. +    HRESULT result{ }; +    result = _vertexTable->SetMatrix( +                         _device.Get(),   // direct3d device +                         "worldMatrix",   // matrix name in the shader +                          &_worldMatrix); // pointer to the matrix +    if (FAILED(result)) +        return false; + +    D3DXMATRIX view{ }; +    D3DXMatrixLookAtLH(&view, &D3DXVECTOR3{ 0.0f, 0.0f, -20.0f }, +           &D3DXVECTOR3{ 0.0f, 0.0f, 0.0f }, &D3DXVECTOR3{ 0.0f, 1.0f, 0.0f }); +    // Do the same for the view matrix. +    result = _vertexTable->SetMatrix( +	                       _device.Get(), // direct 3d device +	                       "viewMatrix",  // matrix name +	                       &view);        // matrix +    if (FAILED(result)) +        return false; + +    D3DXMATRIX projection{ }; +    D3DXMatrixPerspectiveFovLH(&projection, D3DXToRadian(60.0f), +        1024.0f / 768.0f, 0.0f, 100.0f); +    // And also for the projection matrix. +    result = _vertexTable->SetMatrix( +	                       _device.Get(), +	                       "projectionMatrix", +	                       &projection); +    if (FAILED(result)) +        return false; + +    D3DXMatrixRotationY(&_rotationMatrix, D3DXToRadian(0.5f)); +    return true; +} +// ... +// Vertex and index buffer creation aswell as initialization stay unchanged. +// ... +// After checking that shader model 3.0 is available we have to compile and +// create the shaders. +// Declare two temporary buffers storing the compiled shader code. +ID3DXBuffer* vertexShaderBuffer{ }; +ID3DXBuffer* pixelShaderBuffer{ }; +result = D3DXCompileShaderFromFile("vertex.hlsl",  // shader name +                                   nullptr,        // macro definitions +                                   nullptr,        // special includes +                                   "main",         // entry point name +                                   "vs_3_0",       // shader model version +                                   0,              // special flags +                                   &vertexShaderBuffer, // code buffer +                                   nullptr,        // error message +                                   &_vertexTable); // constant table +if (FAILED(result)) +    return -1; +// After the vertex shader compile the pixel shader. +result = D3DXCompileShaderFromFile("pixel.hlsl", +                                   nullptr, +                                   nullptr, +                                   "main", +                                   "ps_3_0", // pixel shader model 3.0 +                                   0, +                                   &pixelShaderBuffer, +                                   nullptr, +                                   nullptr); // no need for a constant table +if (FAILED(result)) +    return -1; +// Create the vertex shader from the code buffer. +result = _device->CreateVertexShader( +             (DWORD*)vertexShaderBuffer->GetBufferPointer(), // code buffer +             &_vertexShader); // vertex shader pointer +if (FAILED(result)) +    return -1; +	 +result = _device->CreatePixelShader( +             (DWORD*)pixelShaderBuffer->GetBufferPointer(), +             &_pixelShader); +if (FAILED(result)) +    return -1; +// Release the temporary code buffers after the shaders are created. +vertexShaderBuffer->Release(); +pixelShaderBuffer->Release(); +// Apply the vertex- and pixel shader. +_device->SetVertexShader(_vertexShader.Get()); +_device->SetPixelShader(_pixelShader.Get()); +// Apply the transform after the shaders have been set. +if (!SetupTransform()) +    return -1; +// You can also REMOVE the call so set the lighting render state. +_device->SetRenderState(D3DRS_LIGHTING, false); +``` + +You can find the complete code here: [DirectX - 3](https://pastebin.com/y4NrvawY) + +## Texturing + +```cpp +// First we need to declare a ComPtr for the texture. +ComPtr<IDirect3DTexture9> _texture{ }; +// Then we have to change the vertex struct. +struct VStruct { +    float x, y, z; +    float u, v;      // Add texture u and v coordinates +    D3DCOLOR color; +}; +// In the vertex declaration we have to add the texture coordinates. +// the top left of the texture is u: 0, v: 0. +std::vector<VStruct> vertices { +    VStruct{ -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, ... }, // bottom left +    VStruct{ -1.0f,  1.0f, 1.0f, 0.0f, 0.0f, ... }, // top left +    VStruct{  1.0f,  1.0f, 1.0f, 1.0f, 0.0f, ... }, // top right +    VStruct{  1.0f, -1.0f, 1.0f, 1.0f, 1.0f, ... }  // bottom right +}; +// Next is the vertex declaration. +std::vector<D3DVERTEXELEMENT9> vertexDecl{ +    {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, +    // Add a 2d float vector used for texture coordinates. +    {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, +    // The color offset is not (3 + 2) * sizeof(float) = 20 bytes +    {0, 20, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, +    D3DDECL_END() +}; +// Now we have to load the texture and pass its to the shader. +// ... +_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); +// Create a Direct3D texture from a png file. +result = D3DXCreateTextureFromFile(_device.Get(), // direct3d device +                                   "texture.png", // texture path +                                   &_texture);    // receiving texture pointer +if (FAILED(result)) +    return -1; +// Attach the texture to shader stage 0, which is equal to texture register 0 +// in the pixel shader. +_device->SetTexture(0, _texture.Get()); +``` + +With the main code ready we now have to adjust the shaders to these changes. + +**Vertex Shader** + +```cpp +float4x4 projectionMatrix; +float4x4 viewMatrix; +float4x4 worldMatrix; +// Add the texture coordinates to the vertex shader in- and output. +struct VS_INPUT { +    float3 position : POSITION; +    float2 texcoord : TEXCOORD; +    float4 color : COLOR; +}; + +struct VS_OUTPUT { +    float4 position : POSITION; +    float2 texcoord : TEXCOORD; +    float4 color : COLOR; +}; + +VS_OUTPUT main(VS_INPUT input) { +    VS_OUTPUT output; +     +    output.position = float4(input.position, 1.0f); +    output.position = mul(output.position, worldMatrix);	 +    output.position = mul(output.position, viewMatrix); +    output.position = mul(output.position, projectionMatrix); + +    output.color = input.color; +    // Set the texcoord output to the input. +    output.texcoord = input.texcoord; +     +    return output; +} +``` + +**Pixel Shader** + +```cpp +// Create  a sampler called "sam0" using sampler register 0, which is equal +// to the texture stage 0, to which we passed the texture. +sampler sam0 : register(s0); + +struct PS_INPUT { +    float4 position : POSITION; +    float2 texcoord : TEXCOORD; +    float4 color : COLOR; +}; + +float4 main(PS_INPUT input) : COLOR{ +    // Do a linear interpolation between the texture color and the input color +    // using 75% of the input color. +    // tex2D returns the texture data at the specified texture coordinate. +    return lerp(tex2D(sam0, input.texcoord), input.color, 0.75f); +} +``` + +## Quotes +<sup>[1]</sup>[DirectX - Wikipedia](https://en.wikipedia.org/wiki/DirectX) | 
