This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
2
rate down
16248
views
bookmark
11. Textures

Here's another pretty short lesson on how to load a texture from a file and map it onto geometry!

2676 downloads
##Introduction## Here we will learn how to map textures to our objects. This lesson modifies the Transformations lesson, so if you get lost here, you might want to backtrack to the lesson on transformations to see if you missed something. I will try my best to explain every new and modified line here. I want to mention, we have changed the code to explicitly define the box, by adding vertices to define every triangle. Although this adds a little more code in creating the box and vertex structure, it should make things easier when it comes to texture mapping. In Direct3D, we use a 2D (u,v) coordinate system to map the texture onto an object. The u-axis runs horizontaly to the image, and the v-axis runs vertically, where u is 0-1 (0 being the start of the lenth of the image, and 1 being the end of the length of the image). So half the image's horizontal length is at .5, even if the actual image lenth is 256 pixels. +[http://www.braynzarsoft.net/image/100027][Texture UV] Now what would happen if we changed the value of u and v to over one? say, 2? You guessed it! it would repeat the texture, like shown below. +[http://www.braynzarsoft.net/image/100028][Texture UV over 1] ##Global Declarations## The first new interface is an object which will hold our texture we load from the file. The second will hold our sampler state information, which will be explained soon. ID3D11ShaderResourceView* CubesTexture; ID3D11SamplerState* CubesTexSamplerState; ##Vertex Structure/Input Layout## Lets look at our vertex structure. We have removed the color member, and replaced it with a texture coordinate member. Texture coordinates for 2D textures only need a u and v value, however, there are 3D textures which we will use for a skymap, which uses an extra "w" value. We have also modified the input layout to include a two float element for the texture coordinates, which replaces our color element. struct Vertex //Overloaded Vertex Structure { Vertex(){} Vertex(float x, float y, float z, float u, float v) : pos(x,y,z), texCoord(u, v){} XMFLOAT3 pos; XMFLOAT2 texCoord; }; D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; ##Vertex Structure/Buffer, Indice List## If we do not separate texture coordinates for each triangles vertice, Only the Top and Bottom might be correctly mapped out, this is why we have added vertices for every triangle in our cube, so we can set each of their texture coordinates. You can try to set the texture coordinates with only the 8 vertices we used in the last lesson, but like i said, the texture will not be mapped out correctly on all six sides. Since we have added more vertices, we need to also update the vertex buffer to hold 24 vertices instead of the 8 we had before. Vertex v[] = { // Front Face Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f), // Back Face Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f), Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f), Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f), Vertex(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f), // Top Face Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f), // Bottom Face Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f), Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f), Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 0.0f), // Left Face Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f), Vertex(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f), // Right Face Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 1.0f), }; DWORD indices[] = { // Front Face 0, 1, 2, 0, 2, 3, // Back Face 4, 5, 6, 4, 6, 7, // Top Face 8, 9, 10, 8, 10, 11, // Bottom Face 12, 13, 14, 12, 14, 15, // Left Face 16, 17, 18, 16, 18, 19, // Right Face 20, 21, 22, 20, 22, 23 }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 12 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer); d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 24; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; **Loading the Texture from a File** ( D3DX11CreateShaderResourceViewFromFile() ) Here, towards the bottom of our init scene function, we are loading our texture from a file using the function D3DX11CreateShaderResourceViewFromFile(), which is defined like this: HRESULT D3DX11CreateShaderResourceViewFromFile( __in ID3D11Device *pDevice, __in LPCTSTR pSrcFile, __in D3DX11_IMAGE_LOAD_INFO *pLoadInfo, __in ID3DX11ThreadPump *pPump, __out ID3D11ShaderResourceView **ppShaderResourceView, __out HRESULT *pHResult ); Where each parameter is described below: **pDevice -** *Pointer to our D3D Device.* **pSrcFile -** *The name of our file (and location if its not in the same folder as the exe).* **pLoadInfo -** *A pointer to a D3DX11_IMAGE_LOAD_INFO structure which defines how the texture should be loaded. We can set this to NULL.* **pPump -** *A pointer to a ID3DX11ThreadPump Interface, used only if we wanted multi-threading, and have the program continue running even while this file is being loaded. Putting NULL here makes this function return only when its completed.* **ppShaderResourceView -** *This is a pointer a shader resource view (ID3D11ShaderResourceView) which will hold the data from this texture.* **pHResult -** *This is a returned pointer which will store the result of this function. We talked about HRESULT's before.* hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, L"braynzar.jpg", NULL, NULL, &CubesTexture, NULL ); ##Describing the Sample State## ( D3D11_SAMPLER_DESC ) Here we can describe the sampler state, or how the shader will render the texture. We create a D3D11_SAMPLER_DESC object: typedef struct D3D11_SAMPLER_DESC { D3D11_FILTER Filter; D3D11_TEXTURE_ADDRESS_MODE AddressU; D3D11_TEXTURE_ADDRESS_MODE AddressV; D3D11_TEXTURE_ADDRESS_MODE AddressW; FLOAT MipLODBias; UINT MaxAnisotropy; D3D11_COMPARISON_FUNC ComparisonFunc; FLOAT BorderColor[4]; FLOAT MinLOD; FLOAT MaxLOD; } D3D11_SAMPLER_DESC; Where each member is described below: **Filter -** *A D3D11_FILTER enumerated type describing the filtering method to use.* **AddressU -** *A D3D11_TEXTURE_ADDRESS_MODE enumerated type describing what to do if the u value is larger than 1 or less than 0.* **AddressV -** *A D3D11_TEXTURE_ADDRESS_MODE enumerated type describing what to do if the v value is larger than 1 or less than 0.* **AddressW -** *A D3D11_TEXTURE_ADDRESS_MODE enumerated type describing what to do if the w value is larger than 1 or less than 0.* **MipLODBias -** *Offset from the calculated mipmap level. For example, if Direct3D calculates that a texture should be sampled at mipmap level 3 and MipLODBias is 2, then the texture will be sampled at mipmap level 5.* **MaxAnisotropy -** *Clamping value used if D3D11_FILTER_ANISOTROPIC or D3D11_FILTER_COMPARISON_ANISOTROPIC is specified in Filter. Valid values are between 1 and 16.* **ComparisonFunc -** *An enumerated type D3D11_COMPARISON_FUNC . This will compare sampled mipmap data with another mipmaps sampled data for this texture.* **BorderColor[4] -** *If D3D11_TEXTURE_ADDRESS_BORDER was specified for any of the AddressU,V, or W, Then this is the color of the space between the texture and the edge of the triangle, if u, v or w was greater than 1 or less than 0.* **MinLOD -** *This is the lowest mipmap level to use, where 0 is the most detailed and largest one.* **MaxLOD -** *This is the largest mipmap level to use, where 0 is the most detailed and largest. To use all the mipmaps, you would want to specify a very large number, like FLT_MAX.* If any of the members are not filled out, the default values will be used: Filter MIN_MAG_MIP_LINEAR AddressU Clamp AddressV Clamp AddressW Clamp MinLOD -3.402823466e+38F (-FLT_MAX) MaxLOD 3.402823466e+38F (FLT_MAX) MipMapLODBias 0.0f MaxAnisotropy 16 ComparisonFunc Never BorderColor float4(0.0f,0.0f,0.0f,0.0f) D3D11_SAMPLER_DESC sampDesc; ZeroMemory( &sampDesc, sizeof(sampDesc) ); sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampDesc.MinLOD = 0; sampDesc.MaxLOD = D3D11_FLOAT32_MAX; **Vertex Structure/Input Layout** ( ID3D11Device::CreateSamplerState() ) After we have described our sampler, we need to create it. We can do this by using the CreateSamplerState() method, where the first parameter is our described sampler state, and the second is an interface to put the sampler state into. hr = d3d11Device->CreateSamplerState( &sampDesc, &CubesTexSamplerState ); ##Sending the Sampler State and Texture to the Shader## ( ID3D11DeviceContext::PSSetShaderResources() ) ( ID3D11DeviceContext::PSSetSamplers() ) If we wanted, we only need to send the sampler state and texture to the pixel shader only once per group of objects that use that same sampler state and texture, but a lot of times the objects will have more than one texture, so you will not be able to set it only once per object. Anyway, Since it is the PS that will be using this information, we will be sending it there. We can do this by calling the methods ID3D11DeviceContext::PSSetShaderResources() and ID3D11DeviceContext::PSSetSamplers(). The first parameter of the PSSetShaderResources() method is the slot number we are sending the shader into. The second parameter is the number of elements in our texture array. We set this to one since we are only sending one texture, but we can actually send an array of textures to the shader. The third parameter is an array of ID3D11ShaderResourceView. The PSSetSamplers is similar, except instead of sending an array of ID3D11ShaderResourceView, we are sending an array of ID3D11SamplerState. We only have one sampler state though, so we only need to send one. void DrawScene() { //Clear our backbuffer float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); //Refresh the Depth/Stencil view d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); //Set the WVP matrix and send it to the constant buffer in effect file WVP = cube1World * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); ///////////////**************new**************//////////////////// d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); ///////////////**************new**************//////////////////// //Draw the first cube d3d11DevCon->DrawIndexed( 36, 0, 0 ); WVP = cube2World * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); ///////////////**************new**************//////////////////// d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); ///////////////**************new**************//////////////////// //Draw the second cube d3d11DevCon->DrawIndexed( 36, 0, 0 ); //Present the backbuffer to the screen SwapChain->Present(0, 0); } Now we can load a texture! **Exercise:** 1. Add a small border to your image. 1. Find what the best quality filter is by playing with the sampler description.
Comments
Update for VS 2015 Community Edition https://github.com/Microsoft/DirectXTex 1. download package: https://github.com/Microsoft/DirectXTex/archive/master.zip 2. extract the package, open solution file: "DirectXTex-masterDirectXTex_Desktop_2015_Win10.sln" (VS 2015 Community Edition) 3. build solution for your project (x86/x64, debug/release) -> library is located under "DirectXTex-masterDirectXTexBinDesktop_2015_Win10x64DebugDirectXTex.lib" 4. copy source files into your project folder a. "DirectXTex-masterDDSTextureLoader*.*" (2 files) b. "DirectXTex-masterWICTextureLoader*.*" (2 files) 5. add copied files to your project -> DirectX::CreateWICTextureFromFile is now available 6. add header files directory from DirectXTex to your include path in project properties ("DirectXTex-masterDirectXTex") 7. add library file directory from DirectXTex to your library path in project properties ("DirectXTex-masterDirectXTexBinDesktop_2015_Win10x64Debug") 8. add "DirectXTex.lib" to Linker >> Input in project properties 9. add "my_image.png" to your project folder 10. use DirectXTex: hr = DirectX::CreateWICTextureFromFile(pDevice, L"my_image.png", NULL, &pCubeTexture);
on Feb 18 `16
wlasar64
Thanks wlasar64, Microsoft also has a dds and wic texture loading classes somewhere, which are the ones I've used
on Feb 18 `16
iedoc
Hi, im trying to add texture to my cube, but using WIC and directXTK. Does anyone know how i could do this carrying on from the previous tutorial? Thanks
on Apr 15 `16
andydeveloper27@gmail.com
You should put the shader file Effects.fx in here
on Feb 22 `18
boyga20@gmail.com
You forgot to explain that the shader code needs to be updated.
on Jun 18 `18
a49