DirecX 10 学习笔记4:纹理

DirecX 10 学习笔记4:纹理这一篇的目的是把一幅图像作为纹理,画一个有纹理的三角形。上面的图有点不恰当,实际上,因为每个像素点上都用纹理图像上的像素去渲染,所以在vertexshader里不用设置顶点的颜色了,后面会讲到。关于纹理:纹理文件的格式为.dds(DirectDrawSurface),可以用DirectXSD

大家好,欢迎来到IT知识分享网。DirecX

这一篇的目的是把一幅图像作为纹理,画一个有纹理的三角形。上面的图有点不恰当,实际上,因为每个像素点上都用纹理图像上的像素去渲染,所以在vertex shader里不用设置顶点的颜色了,后面会讲到。

关于纹理:

纹理文件的格式为.dds(Direct Draw Surface),可以用DirectX SDK自带的DirectX Texture Tool创建纹理文件。

在开始看代码前,先简单说明一下纹理映射的工作机制。DirectX通过纹理坐标系统把dds文件中的图像映射到多边形上去。纹理坐标把整幅图像上二维坐标映射到0.0f-1.0f这个区间里。比方说,图像宽256个像素,第一个像素的纹理坐标就是0.0f,第256个像素的纹理坐标时1.0f,第128个像素的纹理坐标就是0.5f。

在纹理坐标系统中,横轴是U,方向是从左到右;纵轴是V,方向是从上到下。比如一块纹理左上角的坐标为U:0.0f,V:0.0f,右下角坐标U:1.0f,V:1.0f

框架的变化:

1、在模型类的底层添加了一个纹理类;

2、把上一篇笔记中的ColorShaderClass替换为TextureShaderClass;

3、color.fx替换为texture.fx

下面先看一下Texture.fx的代码:

在shader中,除了之前就有的三个矩阵,还增加了一个全局变量shaderTexture。这个全局变量是一个纹理资源。我们会在ModelClass中读入dds纹理,并把这个纹理挂到纹理资源shaderTexture上,这样texture shader就能够访问纹理,并用其进行渲染了。

/////////////
// GLOBALS //
/////////////
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
//new
Texture2D shaderTexture;

另外shader中还添加了sampler state。这个东西用来设定在着色时,多边形上的像素是怎样生成的,即采样和插值方式。比如多边形距离视点很远,在屏幕上只占8个像素的面积,而原始的纹理可能是256*256大小的,那这8个像素应该怎样取值呢?这个sampler state就是告诉DirectX,从原始纹理上选择哪些像素,并以怎样的方式组合这些像素,以便生成最终要画到屏幕上的像素值。这里sampler state设置如下:

///////////////////
// SAMPLE STATES //
///////////////////
SamplerState SampleType
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

此处我们只设置了对我们shader而言比较重要的三个属性。实际上DirectX 10中,sampler state包含很多个属性:

typedef struct D3D10_SAMPLER_DESC {
    D3D10_FILTER Filter;
    D3D10_TEXTURE_ADDRESS_MODE AddressU;
    D3D10_TEXTURE_ADDRESS_MODE AddressV;
    D3D10_TEXTURE_ADDRESS_MODE AddressW;
    FLOAT MipLODBias;
    UINT MaxAnisotropy;
    D3D10_COMPARISON_FUNC ComparisonFunc;
    FLOAT BorderColor[4];
    FLOAT MinLOD;
    FLOAT MaxLOD;
} D3D10_SAMPLER_DESC;

对我们来说最重要的属性是Filter,Filter属性决定了采样和插值的方式。本例中我们使用了MIN_MAG_MIP_LINEAR,这种方式虽然计算代价比较大,但视觉效果最佳。它告诉sampler在采样时使用线性插值的方式。AddressU和AddressV两个属性设置为Wrap,以保证坐标值会落在0.0f和1.0f之间。

因为三角形要被渲染为纹理的外观,所以三角形本身不需要具有颜色了。因此VertexInputType和PixelInputType的定义也发生了变化,把原来的颜色属性替换为纹理坐标。纹理坐标具有U和V两个浮点成员,所以这里使用了float2类型。我们可以在TEXCOORD[N]语义中自己定义纹理坐标集的数量。在这个shader里我们只用了一个纹理坐标,所以索引值使用了0。

//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
};
 
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
};

在vertex shader中,唯一的变化是:上一篇笔记中我们取出的是输入顶点的颜色,这里我们取的是纹理坐标:

////////////////////////////////////////////////////////////////////////////////
// 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);
 
    // Store the texture coordinates for the pixel shader.
    output.tex = input.tex;
   
    return output;
}

pixel shader也有变化。这里使用纹理资源的sample函数,把纹理坐标和前面定义的sampler state传进去,这个函数会从要绘制的纹理上取出采样像素,并把这个像素返回:

////////////////////////////////////////////////////////////////////////////////
// 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;
}

technique保持不变。

纹理资源的加载、卸载和访问都是通过TextureClass完成的。对于每个纹理,都需要实例化一个TextureClass类的对象。

TextureClass:

////////////////////////////////////////////////////////////////////////////////
// Filename: textureclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTURECLASS_H_
#define _TEXTURECLASS_H_
 
 
//////////////
// INCLUDES //
//////////////
#include <d3d10.h>
#include <d3dx10.h>
 
 
////////////////////////////////////////////////////////////////////////////////
// Class name: TextureClass
////////////////////////////////////////////////////////////////////////////////
class TextureClass
{
public:
    TextureClass();
    TextureClass(const TextureClass&);
    ~TextureClass();

 
Initialize函数从纹理文件中加载纹理,Shutdown函数用于卸载纹理:
bool Initialize(ID3D10Device*, WCHAR*);
void Shutdown();

GetTexture函数返回纹理资源的指针:

ID3D10ShaderResourceView* GetTexture();

用一个私有指针指向纹理资源:

private:
    ID3D10ShaderResourceView* m_texture;
};
 
#endif

TextureClass的实现:

 

////////////////////////////////////////////////////////////////////////////////
// Filename: textureclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "textureclass.h"
 
TextureClass::TextureClass()
{
    m_texture = NULL;
}
 
 
TextureClass::TextureClass(const TextureClass& other)
{
}
 
 
TextureClass::~TextureClass()
{
}

Initialize以DirectX Device和文件名为参数,把纹理文件挂在纹理资源指针上:

bool TextureClass::Initialize(ID3D10Device* device, WCHAR* filename)
{
    HRESULT result;
 
 
    // Load the texture in.
    result = D3DX10CreateShaderResourceViewFromFile(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;
}
 
ID3D10ShaderResourceView* TextureClass::GetTexture()
{
    return m_texture;
}

由于纹理要作为模型的底层,模型类这时需要进行相应的变动,下面只讨论模型类与上一篇笔记中不同的地方:

和上一篇笔记一样,模型类中要有与shader中定义的顶点类型匹配的顶点定义,上一篇笔记中顶点包含的两个成员是顶点坐标和颜色,这次是顶点坐标和纹理坐标:

////////////////////////////////////////////////////////////////////////////////
// Class name: ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:
    struct VertexType
    {
        D3DXVECTOR3 position;
        D3DXVECTOR2 texture;
    };
 
public:
    ModelClass();
    ModelClass(const ModelClass&);
    ~ModelClass();
 
    bool Initialize(ID3D10Device*, WCHAR*);
    void Shutdown();
    void Render(ID3D10Device*);
 
    int GetIndexCount();

模型类需要能够获取纹理资源,这样它才能把纹理资源传给shader:

 

ID3D10ShaderResourceView* GetTexture();
 
private:
    bool InitializeBuffers(ID3D10Device*);
    void ShutdownBuffers();
    void RenderBuffers(ID3D10Device*);

分别添加加载和释放纹理的私有函数:

bool LoadTexture(ID3D10Device*, WCHAR*);
void ReleaseTexture();

除了上一节的几个私有成员,还增加了一个TextureClass成员,用来在模型对象中访问纹理资源:

private:
    ID3D10Buffer *m_vertexBuffer, *m_indexBuffer;
    int m_vertexCount, m_indexCount;
    TextureClass* m_Texture;
};
 
#endif

ModelClass的实现部分:

////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "modelclass.h"
 
 
ModelClass::ModelClass()
{
    m_vertexBuffer = NULL;
    m_indexBuffer = NULL;
 
    m_Texture = NULL;
}
 
 
ModelClass::ModelClass(const ModelClass& other)
{
}
 
 
ModelClass::~ModelClass()
{
}

Initialize接口和上一节相比,发生了变化,增加了一个文件名参数,把这个参数会和device一起传给Loadtexture函数,用于加载纹理:

bool ModelClass::Initialize(ID3D10Device* 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;
}

ShutDown函数调用ReleaseTexture去释放纹理对象:

void ModelClass::Shutdown()
{
    // Release the model texture.
    ReleaseTexture();
 
    // Release the vertex and index buffers.
    ShutdownBuffers();
 
    return;
}

Render和GetIndexCount函数保持不变:

void ModelClass::Render(ID3D10Device* device)
{
    // Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
    RenderBuffers(device);
 
    return;
}
 
 
int ModelClass::GetIndexCount()
{
    return m_indexCount;
}

GetTexture函数返回纹理资源。shader会用这个纹理资源来渲染模型:

ID3D10ShaderResourceView* ModelClass::GetTexture()
{
    return m_Texture->GetTexture();
}

在InitializeBuffers函数中,我们把上一节中给顶点设置颜色的代码替换为设置改点对应的纹理坐标。纹理坐标的顺序是先U后V。比如这里第一个顶点的纹理坐标对应着纹理图像的左下角,也就是U=0.0,V=1.0f,下面是与上一篇笔记中不同的部分:

// 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);

ShutdownBuffers和RenderBuffers函数维持不变。
LoadTexture函数会创建纹理对象并根据输入的文件名对纹理对象进行初始化,该函数会在初始化阶段调用。
bool ModelClass::LoadTexture(ID3D10Device* 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负责释放纹理对象:

void ModelClass::ReleaseTexture()
{
    // Release the texture object.
    if(m_Texture)
    {
        m_Texture->Shutdown();
        delete m_Texture;
        m_Texture = 0;
    }
 
    return;
}

在上一篇笔记中,shader是由ColorShaderClass加载的,这里因为引入了纹理,所以要对这个类进行一些小改动,

改动后的类名为TextureShaderClass:

这个类与原来的ColorShaderClass相比,多了一个ID3D10EffectShaderResourceVariable指针,用这个指针来指向shader的纹理资源。此外Render和SetShaderParameters函数分别多了一个纹理资源指针。TextureShaderClass类的声明如下:

////////////////////////////////////////////////////////////////////////////////
// Filename: textureshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTURESHADERCLASS_H_
#define _TEXTURESHADERCLASS_H_
 
 
//////////////
// INCLUDES //
//////////////
#include <d3d10.h>
#include <d3dx10math.h>
#include <fstream>
using namespace std;
 
 
////////////////////////////////////////////////////////////////////////////////
// Class name: TextureShaderClass
////////////////////////////////////////////////////////////////////////////////
class TextureShaderClass
{
public:
    TextureShaderClass();
    TextureShaderClass(const TextureShaderClass&);
    ~TextureShaderClass();
 
    bool Initialize(ID3D10Device*, HWND);
    void Shutdown();
    void Render(ID3D10Device*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D10ShaderResourceView*);
 
private:
    bool InitializeShader(ID3D10Device*, HWND, WCHAR*);
    void ShutdownShader();
    void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
 
    void SetShaderParameters(D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D10ShaderResourceView*);
    void RenderShader(ID3D10Device*, int);
 
private:
    ID3D10Effect* m_effect;
    ID3D10EffectTechnique* m_technique;
    ID3D10InputLayout* m_layout;
 
    ID3D10EffectMatrixVariable* m_worldMatrixPtr;
    ID3D10EffectMatrixVariable* m_viewMatrixPtr;
    ID3D10EffectMatrixVariable* m_projectionMatrixPtr;
 
    ID3D10EffectShaderResourceVariable* m_texturePtr;
};
 
#endif

TextureShaderClass的构造和析构函数:

////////////////////////////////////////////////////////////////////////////////
// Filename: textureshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "textureshaderclass.h"
 
 
TextureShaderClass::TextureShaderClass()
{
    m_effect = NULL;
    m_technique = NULL;
    m_layout = NULL;
 
    m_worldMatrixPtr = NULL;
    m_viewMatrixPtr = NULL;
    m_projectionMatrixPtr = NULL;
 
    m_texturePtr = NULL;
}
 
 
TextureShaderClass::TextureShaderClass(const TextureShaderClass& other)
{
}
 
 
TextureShaderClass::~TextureShaderClass()
{
}

Initialize调用InitializeShader去初始化shader:

bool TextureShaderClass::Initialize(ID3D10Device* device, HWND hwnd)
{
    bool result;
    // Initialize the shader that will be used to draw the triangle.
    result = InitializeShader(device, hwnd, L"../Engine/texture.fx");
    if(!result)
    {
        return false;
    }
 
    return true;
}

Shutdown函数:

void TextureShaderClass::Shutdown()
{
    // Shutdown the shader effect.
    ShutdownShader();
 
    return;
}

Render函数比以前多了个指向纹理资源的指针,该函数会把这个指针传给SetParameters函数,从而可以对shader中的纹理进行设置:

void TextureShaderClass::Render(ID3D10Device* device, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, 
                D3DXMATRIX projectionMatrix, ID3D10ShaderResourceView* texture)
{
    // Set the shader parameters that it will use for rendering.
    SetShaderParameters(worldMatrix, viewMatrix, projectionMatrix, texture);
 
    // Now render the prepared buffers with the shader.
    RenderShader(device, indexCount);
 
    return;
}

InitializeShader中的technique名字有所变化:

 

bool TextureShaderClass::InitializeShader(ID3D10Device* device, HWND hwnd, WCHAR* filename)
{
    HRESULT result;
    ID3D10Blob* errorMessage;
    D3D10_INPUT_ELEMENT_DESC polygonLayout[2];
    unsigned int numElements;
    D3D10_PASS_DESC passDesc;
 
 
    // Initialize the error message.
    errorMessage = 0;
 
    // Load the shader in from the file.
    result = D3DX10CreateEffectFromFile(filename, NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, 
                        device, NULL, NULL, &m_effect, &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, filename);
        }
        // If there was  nothing in the error message then it simply could not find the shader file itself.
        else
        {
            MessageBox(hwnd, filename, L"Missing Shader File", MB_OK);
        }
 
        return false;
    }
 
    // Get a pointer to the technique inside the shader.
    m_technique = m_effect->GetTechniqueByName("TextureTechnique");
    if(!m_technique)
    {
        return false;
    }

此外,因为顶点格式发生了变化,所以input layout也要有相应的调整。其第二个元素不再是颜色而是纹理坐标,因此其SemanticName改为TEXCOORD,Format改为DXGI_FORMAT_R32G32_FLOAT。

// Now setup the layout of the data that goes into the shader.
// 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 = D3D10_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 = D3D10_APPEND_ALIGNED_ELEMENT;
polygonLayout[1].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
polygonLayout[1].InstanceDataStepRate = 0;
 
// Get a count of the elements in the layout.
numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
 
// Get the description of the first pass described in the shader technique.
m_technique->GetPassByIndex(0)->GetDesc(&passDesc);
 
// Create the input layout.
result = device->CreateInputLayout(polygonLayout, numElements, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_layout);
if(FAILED(result))
{
    return false;
}
 
// Get pointers to the three matrices inside the shader so we can update them from this class.
m_worldMatrixPtr = m_effect->GetVariableByName("worldMatrix")->AsMatrix();
m_viewMatrixPtr = m_effect->GetVariableByName("viewMatrix")->AsMatrix();
m_projectionMatrixPtr = m_effect->GetVariableByName("projectionMatrix")->AsMatrix();

因为shader中多了一个纹理资源全局变量,所以在InitializeShader中函数中也要获取这个全局变量的指针以便后面对其进行设置:

// Get pointer to the texture resource inside the shader.
m_texturePtr = m_effect->GetVariableByName("shaderTexture")->AsShaderResource();
 
return true;
}

ShutdownShader函数:

 

void TextureShaderClass::ShutdownShader()
{
    // Release the pointer to the texture in the shader file.
    m_texturePtr = NULL;
 
    // Release the pointers to the matrices inside the shader.
    m_worldMatrixPtr = NULL;
    m_viewMatrixPtr = NULL;
    m_projectionMatrixPtr = NULL;
 
    // Release the pointer to the shader layout.
    if(m_layout)
    {
        m_layout->Release();
        m_layout = 0;
    }
 
    // Release the pointer to the shader technique.
    m_technique = 0;
 
    // Release the pointer to the shader.
    if(m_effect)
    {
        m_effect->Release();
        m_effect = 0;
    }
 
    return;
}

OutputShaderErrorMessage保持不变。

SetShaderParameters函数增加了一个指向纹理资源的指针,这个指针指向的纹理资源会传给shader用来渲染:

void TextureShaderClass::SetShaderParameters(D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix,
                         ID3D10ShaderResourceView* texture)
{
    // Set the world matrix variable inside the shader.
    m_worldMatrixPtr->SetMatrix((float*)&worldMatrix);
 
    // Set the view matrix variable inside the shader.
    m_viewMatrixPtr->SetMatrix((float*)&viewMatrix);
 
    // Set the projection matrix variable inside the shader.
    m_projectionMatrixPtr->SetMatrix((float*)&projectionMatrix);
 
    // Bind the texture.
    m_texturePtr->SetResource(texture);
 
    return;
}

RenderShader保持不变。

最后是在文档视图框架中的相应改动:

由于原来的ColorShaderClass没有了,所以要把视图类中的ColorShaderClass替换为新的TextureShaderClass成员:

TextureShaderClass* m_TextureShader;

在InitializeShader成员函数中修改模型对象的初始化函数,由原来的:

result = pmesh->Initialize(m_device);

改为新的:

result = pmesh->Initialize(m_device, L"../02_01/data/Jellyfish.dds");

并把ColorShaderClass的初始化代码替换为相应的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;
}

OnPaint函数中:

// Render the model using the texture shader.
m_TextureShader->Render(m_D3D->GetDevice(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture());

原教程地址:http://www.rastertek.com/dx10tut05.html,初次接触DirectX,很多地方不了解,路过的朋友如果发现错误,请多指点。谢谢。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/33683.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信