原英文版地址:http://www.rastertek.com/dx11tut05.html
本教程将解释如何在Directx11中使用纹理。纹理使我们可以通过将照片和其他图像应用到多边形面上,将照片真实性添加到场景中。例如,在本教程中,我们将采用以下图像:
然后将其应用于上一教程中的多边形,以生成以下内容:
我们将使用的纹理格式是.dds文件。这是DirectX使用的直接绘制曲面格式。用于生成.dds文件的工具随DirectX SDK一起提供。它位于DirectX实用程序下,称为DirectX纹理工具。您可以创建任意大小和格式的新纹理,然后剪切并粘贴图像或其他格式纹理,并将其保存为.dds文件。它很容易使用。
在进入代码之前,我们应该讨论纹理映射是如何工作的。要将.dds图像中的像素映射到多边形上,我们使用所谓的texel坐标系。该系统将像素的整数值转换为介于0.0f和1.0f之间的浮点值。例如,如果纹理宽度为256像素,则第一个像素将映射到0.0f,第256个像素将映射到1.0f,中间128个像素将映射到0.5f。
在Texel坐标系中,宽度值命名为“U”,高度值命名为“V”。宽度从左边的0.0到右边的1.0。高度从顶部的0.0到底部的1.0。例如,左上角表示为U 0.0,V 0.0,右下角表示为U 1.0,V 1.0。我做了一个下图来说明这个系统:
现在我们已经基本了解了如何将纹理映射到多边形上,我们可以查看本教程的更新框架:
自上一个教程以来,对框架的更改是modelclass中的新textureClass和替换colorshaderclass的新textureshaderClass。我们将首先通过查看新的HLSL纹理明暗器来开始代码部分。
文字与视觉
纹理顶点明暗器类似于上一个颜色明暗器,只是有一些更改以适应纹理。
// Filename: texture.vs
/
// GLOBALS //
/
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};
我们不再在顶点类型中使用颜色,而是使用纹理坐标。因为纹理坐标采用U和V浮动坐标,所以我们使用float2作为其类型。纹理坐标的语义是顶点着色和像素着色的texcoord0。可以将零更改为任意数字,以指示允许使用多个纹理坐标时使用的坐标集。
//
// 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);
与上一教程中的“颜色顶点”明暗器相比,“纹理顶点”明暗器的唯一区别是,我们不从输入顶点获取颜色的副本,而是获取纹理坐标的副本并将其传递给像素明暗器。
//存储像素明暗器的纹理坐标。
output.tex = input.tex;
return output;
}
Texture.ps
// Filename: texture.ps
纹理像素明暗器有两个全局变量。第一个是纹理2d shadertexture,它是纹理资源。这将是我们的纹理资源,用于在模型上渲染纹理。第二个新变量是samplerstate sampletype。取样器状态允许我们修改像素在着色时如何写入多边形面。例如,如果多边形真的离得很远,并且在屏幕上只占8个像素,那么我们将使用采样状态来确定哪些像素或者哪些像素组合将实际从原始纹理中绘制出来。原始纹理可能是256像素乘256像素,因此决定绘制哪个像素非常重要,以确保纹理在非常小的多边形面上仍然看起来不错。我们还将在textureshaderClass中设置取样器状态,然后将其附加到资源指针上,以便该像素明暗器可以使用它来确定要绘制的像素样本。
/
// GLOBALS //
/
Texture2D shaderTexture;
SamplerState SampleType;
纹理像素明暗器的PixelInputType也使用纹理坐标而不是颜色值进行修改。
//
// TYPEDEFS //
//
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
像素明暗器已被修改,因此它现在使用hlsl sample函数。sample函数使用我们在上面定义的采样器状态和该像素的纹理坐标。它使用这两个变量来确定并返回多边形面上此UV位置的像素值。
// 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
纹理类封装了单个纹理资源的加载、卸载和访问。对于每个需要的纹理,必须实例化此类的对象。
// 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函数返回一个指向纹理资源的指针,以便它可以用于着色。
ID3D11ShaderResourceView* GetTexture();
private:
This is the private texture resource.
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设备和文件名,然后将纹理文件加载到名为m_texture的shader资源变量中。现在可以使用纹理进行渲染。
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;
}
shutdown函数释放纹理资源(如果已加载),然后将指针设置为空。
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头包含在ModelClass头中。
///
// 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();
ModelClass还有一个getTexture函数,因此它可以将自己的纹理资源传递给将绘制此模型的明暗器。
ID3D11ShaderResourceView* GetTexture();
private:
bool InitializeBuffers(ID3D11Device*);
void ShutdownBuffers();
void RenderBuffers(ID3D11DeviceContext*);
ModelClass同时具有私有的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;
The class constructor now initializes the new texture object to null.
m_Texture = 0;
}
ModelClass::ModelClass(const ModelClass& other)
{
}
ModelClass::~ModelClass()
{
}
初始化现在将模型将使用的.dds纹理的文件名作为输入。
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;
}
The Initialize function now calls a new private function that will load the texture.
// Load the texture for this model.
result = LoadTexture(device, textureFilename);
if(!result)
{
return false;
}
return true;
}
void ModelClass::Shutdown()
{
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。使用本页顶部的图表来确定坐标需要是什么。请注意,可以更改坐标以将纹理的任何部分映射到多边形面的任何部分。在本教程中,我只是为了简单起见而进行直接映射。
//用数据加载顶点数组。
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只是上一个教程中colorshaderClass的更新版本。此类将用于使用顶点和像素明暗器绘制三维模型。
// 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;
采样器状态指针有一个新的私有变量。此指针将用于与纹理明暗器交互。
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;
将为此材质球加载新的texture.vs和texture.ps hlsl文件。
// Initialize the vertex and pixel shaders.
result = InitializeShader(device, hwnd, L"../Engine/texture.vs", L"../Engine/texture.ps");
if(!result)
{
return false;
}
return true;
}
shutdown函数调用shader变量的释放。
void TextureShaderClass::Shutdown()
{
// Shutdown the vertex and pixel shaders as well as the related objects.
ShutdownShader();
return;
}
现在,render函数接受一个名为texture的新参数,该参数是指向纹理资源的指针。然后将其发送到setshaderParameters函数中,以便可以在着色器中设置纹理,然后用于渲染。
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设置纹理明暗器。
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;
加载新的纹理顶点和像素明暗器。
// 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_格式_r32g32_float。现在,这两个更改将使此布局与模型类定义中的新VertexType和明暗器文件中的typedef对齐。
// 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,它的处理成本更高,但提供了最佳的视觉效果。它告诉采样器使用线性插值进行缩小、放大和MIP级采样。
AddressU和AddressV设置为Wrap,以确保坐标保持在0.0f和1.0f之间。该范围之外的任何内容都将环绕并放置在0.0f和1.0f之间。采样器状态描述的所有其他设置都是默认设置。
//创建纹理采样器状态描述。
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()
{
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明暗器,则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函数现在接受一个指向纹理资源的指针,然后使用新的纹理资源指针将其分配给着色器。请注意,在渲染缓冲区之前必须设置纹理。
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;
}
render 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;
A new TextureShaderClass private object has been added.
TextureShaderClass* m_TextureShader;
};
#endif
Graphicsclass.cpp
// Filename: graphicsclass.cpp
#include "graphicsclass.h"
在构造函数中,m_textureshader变量设置为空。
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()
{
在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());
现在调用纹理明暗器而不是颜色明暗器来渲染模型。注意,它还从模型中获取纹理资源指针,因此纹理明暗器可以访问模型对象中的纹理。
// 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;
}
总结
现在,您应该了解加载纹理、将纹理映射到多边形面,然后使用明暗器渲染纹理的基本知识。
做练习
1。重新编译代码,确保屏幕上出现纹理映射三角形。完成后按Escape退出。
2。创建自己的dds纹理,并将其与seafloor.dds放在同一目录中。在graphicsClass::initialize函数内,将模型初始化更改为具有纹理名称,然后重新编译并运行程序。
三。更改代码以创建形成正方形的两个三角形。将整个纹理映射到此正方形,以便整个纹理在屏幕上正确显示。
4。将相机移动到不同的距离,以查看Min_Mag_Mip_线性滤波器的效果。
5。尝试其他一些过滤器,将相机移动到不同的距离,以查看不同的结果。
(如果要看其他课的中文翻译版,请到我博客目录查找,我会抽时间把后续的课目都翻译出来,这取决于我有空闲时间。)
时间仓促,只是粗略翻译,可能有多处失误,请谅解。朋友如有发现哪里有错误,欢迎指正,联·系w新licheng16886