summaryrefslogtreecommitdiffhomepage
path: root/directx9.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'directx9.html.markdown')
-rw-r--r--directx9.html.markdown827
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)