目录:http://blog.csdn.net/tdl1001/article/details/7888182
这一节介绍如何在DirectX11中使用贴图。贴图是用于绘制多面体表面细节的图片。贴图技术将照片级真实的效果添加到我们的场景中。这一节我们会使用这张图片作为讲解:
在上一节完成的案例中应用贴图技术来产生这样的画面:
我们将使用的图片是一个dds文件。这是DirectX用于Direct Draw Surface的一种格式。DirectX SDK提供了一个工具来制作dds文件,DirectX Utilities分类下的DirectX Texture Tool。你可以制作任何尺寸和格式的贴图,然后将自己图片或其它格式的贴图粘贴进去,保存为dds文件即可。使用方法很简单。
在编码之前讨论一下贴图系统怎么工作。我们使用了一种纹理坐标系统(Texel Coordinate System)来讲dds文件里的像素映射到多边形上。这个系统将像素的整形值转化为从0.0到1.0之间的浮点数。例如贴图宽为256像素,第一个像素就映射为0.0,第256个像素映射为1.0,然后128个像素映射为0.5.
在纹理坐标系统中,宽的方向坐标为U,高的方向坐标为V。宽度从最左边的0.0到最右边的1.0。高从最上面的0.0到最下面的1.0。例如左上角定义为U:0,V:0,右下角为U:1.0,V:1.0。下面这张图可以形容:
现在我们应该对于怎么将贴图映射到多边形有一个基本的印象。现在可以将整个框架更新一下:
与上节相比,在ModelClass里增加了TextureClass,并且用TextureShaderClass替换掉了ColorShaderClass。下面我们通过先看一下TextuerShader的HLSL代码来开始编码环节
Texture.vs
vertex shader与上一节相比变化不大,仅为了实现贴图做了点小变化。
// Filename: texture.vs
/
// GLOBALS //
/
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};
我们以后不在顶点类型使用颜色了,取而代之使用了纹理坐标。由于纹理坐标仅使用UV两个浮点数,我们采用了float2类型来存储。vertex shander和pixel shader中纹理坐标的语义为TEXCOORD0。我们可以使用多纹理坐标,所以你可以把0更换为任何数字来使用另一组纹理坐标。
//
// TYPEDEFS //
//
struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
};
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
// Vertex Shader
PixelInputType TextureVertexShader(VertexInputType input)
{
PixelInputType output;
// Change the position vector to be 4 units for proper matrix calculations.
input.position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices.
output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
与上一节中的color vertex shader相比,texture vertex shader唯一的区别是不再保存输入顶点的颜色,而保存纹理坐标并传递给pixel shader
// Store the texture coordinates for the pixel shader.
output.tex = input.tex;
return output;
}
Texture.ps
// Filename: texture.ps
texture pixel shader有两个全局变量。第一个是Texture2D类型的shaderTexture,保存了用于在模型上进行渲染的贴图资源。第二个是采样器状态(SamplerState)类型的SampleType,它描述了渲染时怎样将贴图上的像素绘制到多边形上。举例来说,如果多边形很远,绘制时在屏幕上仅有8个像素,采样器状态来指明原贴图中那个像素的颜色或如何混合多个像素后的颜色会用于绘制。原贴图有256x256个像素,必须选对要绘制的像素才能保证最终绘制的小物体看起来真实,因此如何选择非常重要。TextureShaderClass会设置采样器状态,然后把它绑定到资源指针(resource pointer,显卡资源在内存中映射的(对象的?)指针),这样给就能把采样器状态传递给pixel shader以绝对如何选择像素进行绘制。
/
// GLOBALS //
/
Texture2D shaderTexture;
SamplerState SampleType;
为了给Pixel shader使用,pixelInputType也用纹理坐标替代了颜色值。
//
// TYPEDEFS //
//
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
pixel shader也开始使用HLSL采样函数。采样函数以我们上面提到的采样器状态和多边形上当前绘制的点的纹理坐标作为输入。它通过这两个变量决定返回什么颜色。
// Pixel Shader
float4 TexturePixelShader(PixelInputType input) : SV_TARGET
{
float4 textureColor;
// Sample the pixel color from the texture using the sampler at this texture coordinate location.
textureColor = shaderTexture.Sample(SampleType, input.tex);
return textureColor;
}
Textureclass.h
TextureClass封装了如何加载、卸载和存取一个贴图资源。每一个贴图都需要该类的一个对象。
// Filename: textureclass.h
#ifndef _TEXTURECLASS_H_
#define _TEXTURECLASS_H_
//
// INCLUDES //
//
#include <d3d11.h>
#include <d3dx11tex.h>
// Class name: TextureClass
class TextureClass
{
public:
TextureClass();
TextureClass(const TextureClass&);
~TextureClass();
下面两个函数分别通过给定的文件名加载贴图和不再使用时卸载贴图。
bool Initialize(ID3D11Device*, WCHAR*);
void Shutdown();
GetTexture函数返回一个指向贴图资源的指针给shader渲染时使用。
ID3D11ShaderResourceView* GetTexture();
private:
下面是私有变量,保存了贴图资源指针。
ID3D11ShaderResourceView* m_texture;
};
#endif
Textureclass.cpp
// Filename: textureclass.cpp
#include "textureclass.h"
构造函数将贴图资源指针初始化为空
TextureClass::TextureClass()
{
m_texture = 0;
}
TextureClass::TextureClass(const TextureClass& other)
{
}
TextureClass::~TextureClass()
{
}
初始化函数利用Direct3D设备加载给定文件名的文件到shader资源变量m_texture里。加载后,贴图就可以用于渲染了。
bool TextureClass::Initialize(ID3D11Device* device, WCHAR* filename)
{
HRESULT result;
// Load the texture in.
result = D3DX11CreateShaderResourceViewFromFile(device, filename, NULL, NULL, &m_texture, NULL);
if(FAILED(result))
{
return false;
}
return true;
}
清理函数释放已加载的贴图资源并将指针设为空。
void TextureClass::Shutdown()
{
// Release the texture resource.
if(m_texture)
{
m_texture->Release();
m_texture = 0;
}
return;
}
当其它对象需要使用贴图进行渲染时,可以调用GetTexture函数来获取贴图资源。
ID3D11ShaderResourceView* TextureClass::GetTexture()
{
return m_texture;
}
Modelclass.h
由于开始使用贴图,ModelClass也发生了一些变化。
// Filename: modelclass.h
#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_
//
// INCLUDES //
//
#include <d3d11.h>
#include <d3dx10math.h>
首先,把TextureClass的头文件包含了进来
/// // MY CLASS INCLUDES // /// #include "textureclass.h" // Class name: ModelClass class ModelClass { private:
然后,VertexType中也用纹理坐标替换掉颜色。
struct VertexType { D3DXVECTOR3 position; D3DXVECTOR2 texture; }; public: ModelClass(); ModelClass(const ModelClass&); ~ModelClass(); bool Initialize(ID3D11Device*, WCHAR*); void Shutdown(); void Render(ID3D11DeviceContext*); int GetIndexCount();
增加了GetTexture方法,用于绘制时可以提供需要使用的贴图资源。
ID3D11ShaderResourceView* GetTexture(); private: bool InitializeBuffers(ID3D11Device*); void ShutdownBuffers(); void RenderBuffers(ID3D11DeviceContext*);
还有两个私有成员函数LoadTexture、ReleaseTexture来加载、卸载使用的贴图。
bool LoadTexture(ID3D11Device*, WCHAR*); void ReleaseTexture(); private: ID3D11Buffer *m_vertexBuffer, *m_indexBuffer; int m_vertexCount, m_indexCount;
m_Texture负责加载、释放、存储贴图资源。
TextureClass* m_Texture; }; #endif
Modelclass.cpp
// Filename: modelclass.cpp
#include "modelclass.h"
ModelClass::ModelClass()
{
m_vertexBuffer = 0;
m_indexBuffer = 0;
构造函数设置新变量m_texture为空。
m_Texture = 0; } ModelClass::ModelClass(const ModelClass& other) { } ModelClass::~ModelClass() { }
初始化函数现在需要获得贴图文件的路径。
bool ModelClass::Initialize(ID3D11Device* device, WCHAR* textureFilename) { bool result; // Initialize the vertex and index buffer that hold the geometry for the triangle. result = InitializeBuffers(device); if(!result) { return false; }
初始化函数会调用增加的私有函数来完成贴图的加载。
// Load the texture for this model. result = LoadTexture(device, textureFilename); if(!result) { return false; } return true; } void ModelClass::Shutdown() {
清理函数也会调用增加的私有函数来释放初始化时加载的贴图对象。
// Release the model texture. ReleaseTexture(); // Release the vertex and index buffers. ShutdownBuffers(); return; } void ModelClass::Render(ID3D11DeviceContext* deviceContext) { // Put the vertex and index buffers on the graphics pipeline to prepare them for drawing. RenderBuffers(deviceContext); return; } int ModelClass::GetIndexCount() { return m_indexCount; }
渲染模型时需要使用贴图资源,GetTexture负责返回贴图资源。
ID3D11ShaderResourceView* ModelClass::GetTexture() { return m_Texture->GetTexture(); } bool ModelClass::InitializeBuffers(ID3D11Device* device) { VertexType* vertices; unsigned long* indices; D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; D3D11_SUBRESOURCE_DATA vertexData, indexData; HRESULT result; // Set the number of vertices in the vertex array. m_vertexCount = 3; // Set the number of indices in the index array. m_indexCount = 3; // Create the vertex array. vertices = new VertexType[m_vertexCount]; if(!vertices) { return false; } // Create the index array. indices = new unsigned long[m_indexCount]; if(!indices) { return false; }
顶点数组不再需要颜色变量,而需要纹理坐标变量。纹理坐标中U在前V在后。例如第一个纹理坐标是三角形左下角,这个位置对应的U是0.0,V是1.0。你可以通过这节最上面给出的图示找出其它地方的UV。事实上你可以通过更改纹理坐标来将多边形上任意一点映射到贴图的任意一个像素,但为了便于理解,这节将三角形和贴图平行对应。
// Load the vertex array with data. vertices[0].position = D3DXVECTOR3(-1.0f, -1.0f, 0.0f); // Bottom left. vertices[0].texture = D3DXVECTOR2(0.0f, 1.0f); vertices[1].position = D3DXVECTOR3(0.0f, 1.0f, 0.0f); // Top middle. vertices[1].texture = D3DXVECTOR2(0.5f, 0.0f); vertices[2].position = D3DXVECTOR3(1.0f, -1.0f, 0.0f); // Bottom right. vertices[2].texture = D3DXVECTOR2(1.0f, 1.0f); // Load the index array with data. indices[0] = 0; // Bottom left. indices[1] = 1; // Top middle. indices[2] = 2; // Bottom right. // Set up the description of the vertex buffer. vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; vertexBufferDesc.StructureByteStride = 0; // Give the subresource structure a pointer to the vertex data. vertexData.pSysMem = vertices; vertexData.SysMemPitch = 0; vertexData.SysMemSlicePitch = 0; // Now create the vertex buffer. result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer); if(FAILED(result)) { return false; } // Set up the description of the static index buffer. indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; indexBufferDesc.StructureByteStride = 0; // Give the subresource structure a pointer to the index data. indexData.pSysMem = indices; // Create the index buffer. result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer); if(FAILED(result)) { return false; } // Release the arrays now that the vertex and index buffers have been created and loaded. delete [] vertices; vertices = 0; delete [] indices; indices = 0; return true; } void ModelClass::ShutdownBuffers() { // Release the index buffer. if(m_indexBuffer) { m_indexBuffer->Release(); m_indexBuffer = 0; } // Release the vertex buffer. if(m_vertexBuffer) { m_vertexBuffer->Release(); m_vertexBuffer = 0; } return; } void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext) { unsigned int stride; unsigned int offset; // Set vertex buffer stride and offset. stride = sizeof(VertexType); offset = 0; // Set the vertex buffer to active in the input assembler so it can be rendered. deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset); // Set the index buffer to active in the input assembler so it can be rendered. deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0); // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles. deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); return; }
LoadTexture负责创建贴图对象并用文件名进行初始化,在初始化函数中会调用这个方法。
bool ModelClass::LoadTexture(ID3D11Device* device, WCHAR* filename)
{
bool result;
// Create the texture object.
m_Texture = new TextureClass;
if(!m_Texture)
{
return false;
}
// Initialize the texture object.
result = m_Texture->Initialize(device, filename);
if(!result)
{
return false;
}
return true;
}
ReleaseTexture负责释放LoadTexture中创建的贴图资源。
void ModelClass::ReleaseTexture()
{
// Release the texture object.
if(m_Texture)
{
m_Texture->Shutdown();
delete m_Texture;
m_Texture = 0;
}
return;
}
Textureshaderclass.h
TextureShaderClass从上节的更新版本,用于使用vertex shader和pixel shader渲染模型。
// Filename: textureshaderclass.h
#ifndef _TEXTURESHADERCLASS_H_
#define _TEXTURESHADERCLASS_H_
//
// INCLUDES //
//
#include <d3d11.h>
#include <d3dx10math.h>
#include <d3dx11async.h>
#include <fstream>
using namespace std;
// Class name: TextureShaderClass
class TextureShaderClass
{
private:
struct MatrixBufferType
{
D3DXMATRIX world;
D3DXMATRIX view;
D3DXMATRIX projection;
};
public:
TextureShaderClass();
TextureShaderClass(const TextureShaderClass&);
~TextureShaderClass();
bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*);
private:
bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*);
void ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*);
void RenderShader(ID3D11DeviceContext*, int);
private:
ID3D11VertexShader* m_vertexShader;
ID3D11PixelShader* m_pixelShader;
ID3D11InputLayout* m_layout;
ID3D11Buffer* m_matrixBuffer;
仅增加了一个私有成员,保存采样器状态的指针。texture shader会使用这个变量。
ID3D11SamplerState* m_sampleState;
};
#endif
Textureshaderclass.cpp
// Filename: textureshaderclass.cpp
#include "textureshaderclass.h"
TextureShaderClass::TextureShaderClass()
{
m_vertexShader = 0;
m_pixelShader = 0;
m_layout = 0;
m_matrixBuffer = 0;
构造函数中将新成员设为空。
m_sampleState = 0;
}
TextureShaderClass::TextureShaderClass(const TextureShaderClass& other)
{
}
TextureShaderClass::~TextureShaderClass()
{
}
bool TextureShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
bool result;
初始化将加载使用贴图的新HLSL文件: texture.vs和 texture.ps
// Initialize the vertex and pixel shaders.
result = InitializeShader(device, hwnd, L"../Engine/texture.vs", L"../Engine/texture.ps");
if(!result)
{
return false;
}
return true;
}
清理函数调用释放shader的函数
void TextureShaderClass::Shutdown()
{
// Shutdown the vertex and pixel shaders as well as the related objects.
ShutdownShader();
return;
}
Render函数增加了一个输入变量,该变量指向贴图资源。这个变量会被SetShaderParameters函数来将贴图传给shader渲染。
bool TextureShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture)
{
bool result;
// Set the shader parameters that it will use for rendering.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture);
if(!result)
{
return false;
}
// Now render the prepared buffers with the shader.
RenderShader(deviceContext, indexCount);
return true;
}
InitializeShader设置shader
bool TextureShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
HRESULT result;
ID3D10Blob* errorMessage;
ID3D10Blob* vertexShaderBuffer;
ID3D10Blob* pixelShaderBuffer;
D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
unsigned int numElements;
D3D11_BUFFER_DESC matrixBufferDesc;
新变量保存对贴图采样器的描述,用于后续创建时使用。
D3D11_SAMPLER_DESC samplerDesc;
// Initialize the pointers this function will use to null.
errorMessage = 0;
vertexShaderBuffer = 0;
pixelShaderBuffer = 0;
Load in the new texture vertex and pixel shaders.
// Compile the vertex shader code.
result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "TextureVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
&vertexShaderBuffer, &errorMessage, NULL);
if(FAILED(result))
{
// If the shader failed to compile it should have writen something to the error message.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
}
// If there was nothing in the error message then it simply could not find the shader file itself.
else
{
MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
}
return false;
}
// Compile the pixel shader code.
result = D3DX11CompileFromFile(psFilename, NULL, NULL, "TexturePixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
&pixelShaderBuffer, &errorMessage, NULL);
if(FAILED(result))
{
// If the shader failed to compile it should have writen something to the error message.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
}
// If there was nothing in the error message then it simply could not find the file itself.
else
{
MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
}
return false;
}
// Create the vertex shader from the buffer.
result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader);
if(FAILED(result))
{
return false;
}
// Create the pixel shader from the buffer.
result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader);
if(FAILED(result))
{
return false;
}
输入的顶点信息的数据结中,颜色被纹理坐标替代了。结构中第一个成员仍然是位置,但第二个成员的语义和格式已经变为TEXCOORD 和 DXGI_FORMAT_R32G32_FLOAT。对应的在ModelClass定义和shader文件中的数据类型的定义也要更改。
// Create the vertex input layout description.
// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
polygonLayout[0].SemanticName = "POSITION";
polygonLayout[0].SemanticIndex = 0;
polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[0].InputSlot = 0;
polygonLayout[0].AlignedByteOffset = 0;
polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[0].InstanceDataStepRate = 0;
polygonLayout[1].SemanticName = "TEXCOORD";
polygonLayout[1].SemanticIndex = 0;
polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
polygonLayout[1].InputSlot = 0;
polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[1].InstanceDataStepRate = 0;
// Get a count of the elements in the layout.
numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// Create the vertex input layout.
result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(),
&m_layout);
if(FAILED(result))
{
return false;
}
// Release the vertex shader buffer and pixel shader buffer since they are no longer needed.
vertexShaderBuffer->Release();
vertexShaderBuffer = 0;
pixelShaderBuffer->Release();
pixelShaderBuffer = 0;
// Setup the description of the dynamic matrix constant buffer that is in the vertex shader.
matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixBufferDesc.MiscFlags = 0;
matrixBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
if(FAILED(result))
{
return false;
}
现在开始设置采样器状态的说明,然后把它传给像素着色器。采样状态器的成员中,最重要的是过滤方式。过滤方式决定了在绘制多边形显示在屏幕上的每个像素时,用图片上哪些像素来混合计算出最终的颜色。我使用D3D11_FILTER_MIN_MAG_MIP_LINEAR,告诉采样器在采样小图片,大图片和多级采样时使用线性插值,这种方式在处理时计算量很大,但它能给出最好的效果。
设置AddressU 和AddressV 设置为Wrap(缠绕,循环)来保证采样时使用的坐标值在0.0和1.0之间。超过这个范围的值被绕回替换为一个处以0到1之间的值。其它选项这里采用默认值
// Create a texture sampler state description.
samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
samplerDesc.BorderColor[0] = 0;
samplerDesc.BorderColor[1] = 0;
samplerDesc.BorderColor[2] = 0;
samplerDesc.BorderColor[3] = 0;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// Create the texture sampler state.
result = device->CreateSamplerState(&samplerDesc, &m_sampleState);
if(FAILED(result))
{
return false;
}
return true;
}
ShutdownShader函数释放在TextureShaderClass里用到的所有变量
void TextureShaderClass::ShutdownShader()
{
释放在初始化时创建的着色器状态变量
// Release the sampler state.
if(m_sampleState)
{
m_sampleState->Release();
m_sampleState = 0;
}
// Release the matrix constant buffer.
if(m_matrixBuffer)
{
m_matrixBuffer->Release();
m_matrixBuffer = 0;
}
// Release the layout.
if(m_layout)
{
m_layout->Release();
m_layout = 0;
}
// Release the pixel shader.
if(m_pixelShader)
{
m_pixelShader->Release();
m_pixelShader = 0;
}
// Release the vertex shader.
if(m_vertexShader)
{
m_vertexShader->Release();
m_vertexShader = 0;
}
return;
}
如果HLSL shader不能加载,OutputShaderErrorMessage可以用来在一个文本文件中输出错误信息
void TextureShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename)
{
char* compileErrors;
unsigned long bufferSize, i;
ofstream fout;
// Get a pointer to the error message text buffer.
compileErrors = (char*)(errorMessage->GetBufferPointer());
// Get the length of the message.
bufferSize = errorMessage->GetBufferSize();
// Open a file to write the error message to.
fout.open("shader-error.txt");
// Write out the error message.
for(i=0; i<bufferSize; i++)
{
fout << compileErrors[i];
}
// Close the file.
fout.close();
// Release the error message.
errorMessage->Release();
errorMessage = 0;
// Pop a message up on the screen to notify the user to check the text file for compile errors.
MessageBox(hwnd, L"Error compiling shader. Check shader-error.txt for message.", shaderFilename, MB_OK);
return;
}
SetShaderParameters函数将传入的贴图资源的指针赋给shader。注意必须在绘制物体前设置好贴图资源
bool TextureShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture)
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;
// Transpose the matrices to prepare them for the shader.
D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);
// Lock the constant buffer so it can be written to.
result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the constant buffer.
dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the constant buffer.
dataPtr->world = worldMatrix;
dataPtr->view = viewMatrix;
dataPtr->projection = projectionMatrix;
// Unlock the constant buffer.
deviceContext->Unmap(m_matrixBuffer, 0);
// Set the position of the constant buffer in the vertex shader.
bufferNumber = 0;
// Now set the constant buffer in the vertex shader with the updated values.
deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);
SetShaderParameters函数增加了设置贴图资源的功能
// Set shader texture resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 1, &texture);
return true;
}
RenderShader 调用shader来绘制多边形
void TextureShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
// Set the vertex input layout.
deviceContext->IASetInputLayout(m_layout);
// Set the vertex and pixel shaders that will be used to render this triangle.
deviceContext->VSSetShader(m_vertexShader, NULL, 0);
deviceContext->PSSetShader(m_pixelShader, NULL, 0);
在渲染前,RenderShader还要设置着色器状态
// Set the sampler state in the pixel shader.
deviceContext->PSSetSamplers(0, 1, &m_sampleState);
// Render the triangle.
deviceContext->DrawIndexed(indexCount, 0, 0);
return;
}
Graphicsclass.h
// Filename: graphicsclass.h
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_
///
// MY CLASS INCLUDES //
///
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
GraphicsClass现在引入了TextureShaderClass头文件,并不再使用ColorShaderClass头文件
#include "textureshaderclass.h" / // GLOBALS // / const bool FULL_SCREEN = true; const bool VSYNC_ENABLED = true; const float SCREEN_DEPTH = 1000.0f; const float SCREEN_NEAR = 0.1f; // Class name: GraphicsClass class GraphicsClass { public: GraphicsClass(); GraphicsClass(const GraphicsClass&); ~GraphicsClass(); bool Initialize(int, int, HWND); void Shutdown(); bool Frame(); private: bool Render(); private: D3DClass* m_D3D; CameraClass* m_Camera; ModelClass* m_Model;
增加了一个新的TextureShaderClass类型的私有变量
TextureShaderClass* m_TextureShader; }; #endif
Graphicsclass.cpp
// Filename: graphicsclass.cpp
#include "graphicsclass.h"
构造函数设置 m_TextureShader 为 null
GraphicsClass::GraphicsClass() { m_D3D = 0; m_Camera = 0; m_Model = 0; m_TextureShader = 0; } GraphicsClass::GraphicsClass(const GraphicsClass& other) { } GraphicsClass::~GraphicsClass() { } bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd) { bool result; // Create the Direct3D object. m_D3D = new D3DClass; if(!m_D3D) { return false; } // Initialize the Direct3D object. result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR); if(!result) { MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK); return false; } // Create the camera object. m_Camera = new CameraClass; if(!m_Camera) { return false; } // Create the model object. m_Model = new ModelClass; if(!m_Model) { return false; }
将渲染模型使用的贴图文件名传递给ModelClass::Initialize函数
// Initialize the model object. result = m_Model->Initialize(m_D3D->GetDevice(), L"../Engine/data/seafloor.dds"); if(!result) { MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK); return false; }
创建并初始化 TextureShaderClass 对象
// Create the texture shader object. m_TextureShader = new TextureShaderClass; if(!m_TextureShader) { return false; } // Initialize the texture shader object. result = m_TextureShader->Initialize(m_D3D->GetDevice(), hwnd); if(!result) { MessageBox(hwnd, L"Could not initialize the texture shader object.", L"Error", MB_OK); return false; } return true; } void GraphicsClass::Shutdown() {
释放TextureShaderClass对象
// Release the texture shader object. if(m_TextureShader) { m_TextureShader->Shutdown(); delete m_TextureShader; m_TextureShader = 0; } // Release the model object. if(m_Model) { m_Model->Shutdown(); delete m_Model; m_Model = 0; } // Release the camera object. if(m_Camera) { delete m_Camera; m_Camera = 0; } // Release the Direct3D object. if(m_D3D) { m_D3D->Shutdown(); delete m_D3D; m_D3D = 0; } return; } bool GraphicsClass::Frame() { bool result; // Render the graphics scene. result = Render(); if(!result) { return false; } return true; } bool GraphicsClass::Render() { D3DXMATRIX viewMatrix, projectionMatrix, worldMatrix; bool result; // Clear the buffers to begin the scene. m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f); // Generate the view matrix based on the camera's position. m_Camera->Render(); // Get the view, projection, and world matrices from the camera and d3d objects. m_Camera->GetViewMatrix(viewMatrix); m_D3D->GetProjectionMatrix(projectionMatrix); m_D3D->GetWorldMatrix(worldMatrix); // Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing. m_Model->Render(m_D3D->GetDevice());
不再使用color shader了,现在使用texture shader。注意需要从model变量获取贴图资源,并传递给TextureShader的渲染函数。
// Render the model using the texture shader. result = m_TextureShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture()); if(!result) { return false; } // Present the rendered scene to the screen. m_D3D->EndScene(); return true; }
小结
现在我们了解了加载贴图,映射到多边形并用shader渲染的基本概念了。