Directx11学习笔记【十七】纹理贴图

本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5596180.htmlhtml

     

      在以前的例子中,咱们实现了光照和材质使得场景大大增长了真实感,然而材质提供的细节只是在顶点级别上,要想在像素级别提供细节还得借助于纹理,此次让咱们学习dx11中一些有关纹理的基础。c++

1.纹理坐标

1imagegit

       在direct3d中,纹理坐标用一个二维向量(u,v)表示,纹理左上角为原点,u正方向沿纹理水平向右,v正方向沿纹理垂直向下,且0<=u,v<=1。因此,(0,0)表明左上角,(1,1)表明右下角,(0.5,0.25)表明图中点的位置。这样,咱们在指定纹理坐标时就不须要考虑纹理大小了,不管对于256*256,512*512仍是1024*1024的纹理,(0.5,0.5)表示的都是正中间。github

       若是给出了一个三角形三个顶点对应的纹理坐标,那么三角形内部的纹理坐标就须要插值来计算了。以下图所示,一个三角形三个顶点(p1,p2,p3)对应纹理坐标(q1,q2,q3),那么三角形内部任意一点(x,y,z)=p0 + s(p1 - p0) + t(p2 - p0),那么对应的纹理坐标(u,v)=q0 + s(q1 - q0) + t(q2 - q0)。api

image

2.纹理过滤

       纹理贴图的元素其实是由离散的颜色值组成的,而不能看作是一块矩形区域。理想状况是一张512*512纹理刚好投影在一块512*512大小的屏幕上,一个像素对应一个纹理值,可是一般状况并非这样的。当屏幕像素不能和纹理值一一对应时应该怎么作呢?这种状况也分为两种:一种是摄像机不断靠近纹理,这时一张很小的纹理就可能覆盖整个屏幕,因而一个纹理值就要对应不少个像素;第二种状况偏偏相反,摄像机不断远离纹理图片,当投影在屏幕的足够小时,就有可能不少纹理值会投影在同一像素上。这种状况,就须要纹理过滤,两种状况也对应两种纹理过滤方式:Magnification和Minification状况。数组

2.1放大(Magnification)

      假设一张 256*256的纹理覆盖了1024*1024的屏幕大小,那么这时一个纹理对应16个屏幕像素,这16个像素对应同一个纹理值,如何取得颜色呢?d3d主要有两种过滤方式:取最近点和线性插值。app

2.1.1取最近点(Nearest neighbor point)

      对于任意像素对应的纹理坐标值,取距离最近的纹理值。框架

     image

2.1.2线性插值(Linear interpolate)

      与坐标最近的4个纹理值进行插值获得最终结果。看下图就很清楚了:函数

image

image

2.2缩小(Minification)

       在Minification状况下,多个纹理元素被投影在屏幕上同一个像素位置。好比一个1024*1024的纹理,投影在屏幕上256*256范围的空间内,平均每一个像素覆盖16个纹理值。这种状况下,最流行的过滤方法称为Mipmaping。在该方法中,使用到一个Mipmap链。Mipmap链是原纹理组成的一个数组,数组中第一个纹理为原始纹理,后面的第一个纹理在u、v尺寸上为上一个纹理的一半,依次计算,直接纹理尺寸为1为止。以下图所示:学习

image

      这样在运行时,硬件会选择合适的纹理。选择合适的纹理也有两种方法:Point Filtering和Linear Filtering。原理与上面说的很相近就再也不说明了。

2.3各向异性过滤(Anisotropic Filtering)

      还有一种高级的过滤方式称为各向异性过滤,这种过滤方式对于视线与物体表面法线成90度时状况的效果比较好。

image

右边图使用了各向异性过滤方式。

3.纹理坐标寻址(Address modes)

     纹理坐标被限制在(0,1)之间,但为顶点指点(0,1)以外的纹理坐标仍然是被容许的,这时解析出来的坐标值就和纹理坐标寻址方式有关了。d3d中支持的纹理寻址方式主要有如下几种:

3.1wrap

     贴图在物体表面重复,相似于咱们设置桌面是图片的平铺方式。当坐标大于1时,经过去掉整数部分,根据获得的小数部分来获得纹理值;坐标小于0,则加上一个最小正数,让坐标大于0。

image

3.2border

     当纹理坐标不在(0,1)范围时,咱们能够手动指定一个值来使用。

image

3.3clamp

       坐标超过正常范围时,使用在(0,1)范围的坐标与该坐标最近的点的纹理值。例如,坐标(u,v),u在(0,1)范围内,而v>1,则使用(u,1)做为最终纹理值。若是u>1,v>1,则使用(1,1)做为纹理值。

image

3.4mirror

      每当纹理坐标越过一个整数值时,使用的纹理与越过整数值以前的纹理成镜像关系。

image

4.使用纹理

        d3d11中纹理对应的接口为ID3D11Texture2D,一个纹理能够在管线的多个阶段使用,使用的时候要先绑定到相应阶段。而真正绑定到管线的不是纹理自己,而是纹理对应的视图。下面来介绍一下c++和HLSL中纹理的使用方式。

4.1 c++中

      在c++读取图片使用纹理主要有两个步骤:建立纹理和建立相应阶段的视图。在dx11龙书中这两个步骤是经过一个函数D3DX11CreateShaderResourceViewFromFile实现的,函数原型以下:

HRESULT D3DX11CreateShaderResourceViewFromFile(
  _In_  ID3D11Device             *pDevice,
  _In_  LPCTSTR                  pSrcFile,
  _In_  D3DX11_IMAGE_LOAD_INFO   *pLoadInfo,
  _In_  ID3DX11ThreadPump        *pPump,
  _Out_ ID3D11ShaderResourceView **ppShaderResourceView,
  _Out_ HRESULT                  *pHResult
);

      可是在最新的sdk中该方法已经废弃了,针对这一点MSDN的解释:

Note  The D3DX (D3DX 9, D3DX 10, and D3DX 11) utility library is deprecated for Windows 8 and is not supported for Windows Store apps.

Note  Instead of using this function, we recommend that you use these:

  • DirectXTK library (runtime), CreateXXXTextureFromFile (where XXX is DDS or WIC)
  • DirectXTex library (tools), LoadFromXXXFile (where XXX is WIC, DDS, or TGA; WIC doesn't support DDS and TGA; D3DX 9 supported TGA as a common art source format for games) then CreateShaderResourceView

     为了加载纹理图片,咱们能够到github上下载DirectXTK或者DirectXTex,使用框架中的方法来代替D3DX11CreateShaderResourceViewFromFile。这里以DirectXTex为例,从github上下载下来,把DDSTextureLoader的代码载入到咱们的工程中,使用CreateDDSTextureFromFile加载纹理图像(纹理格式为.dds,加载其余格式纹理请自行查看文档说明),函数原型以下:

HRESULT CreateDDSTextureFromFile( _In_ ID3D11Device* d3dDevice,
                                      _In_z_ const wchar_t* szFileName,
                                      _Outptr_opt_ ID3D11Resource** texture,
                                      _Outptr_opt_ ID3D11ShaderResourceView** textureView,
                                      _In_ size_t maxsize = 0,
                                      _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr
                                    );

  c++程序中使用:首先定义一个 ID3D11ShaderResourceView接口保存建立的视图, 而后调用CreateDDSTextureFromFile函数建立。

hr = CreateDDSTextureFromFile(m_pd3dDevice, L"Texture/Wood.dds", nullptr, &m_pTexSRV);
    if (FAILED(hr))
    {
        MessageBox(nullptr, L"create texture failed!", L"error",MB_OK);
        return hr;
    }

         调用后相应的视图就建立好了。

         为了将纹理资源传递到effect文件中,咱们还须要定义一个指向effect全局变量的接口:ID3DX11EffectShaderResourceVariable,而后在编译shader时获得:

m_pFxTex = m_pFx->GetVariableByName("g_tex")->AsShaderResource();

 

       在Update函数中调用SetResource方法将建立的纹理视图赋值给effect中的全局变量。

4.2 effect中    

      在HLSL中对应2D纹理的是Texture2D,可是要注意的是Texture2D的定义不能放在cbuffer中

      在c++程序中咱们能够经过ID3D11DeviceContext::PSSetSamplers来设置纹理过滤方式,在effect文件中咱们一样能够设置过滤方式(推荐在effect中设置),设置方法以下:

//设置过滤方式
SamplerState samTex
{
    Filter = MIN_MAG_MIP_LINEAR;
};

  这里Minification、Magnification和Mipmap中纹理过滤方式所有使用线性过滤方法,如须要查看其它过滤方法,能够查看MSDN中D3D11_FILTER的说明。

      因为使用了纹理,顶点结构也须要改变。咱们再也不使用光照了,因此去掉法线,添加纹理坐标:

struct VertexIn
{
    float3 pos : POSITION;
    float2 tex : TEXCOORD;
};

struct VertexOut
{
    float4 posH  : SV_POSITION;
    float2 tex : TEXCOORD;           
};

  顶点着色器很简单:

VertexOut VS(VertexIn vin)
{
    VertexOut vout;    
    vout.posH = mul(float4(vin.pos, 1.0f), g_worldViewProj);    
    vout.tex = vin.tex;    
    return vout;
}

  像素着色器中须要使用纹理资源,经过函数Texture2D::sample(SamplerState samper, float2 texcoord)实现:

float4 PS(VertexOut pin) : SV_Target
{
    float4 texColor = g_tex.Sample(samTex,pin.tex);
    return texColor;
}

5.运行结果

image

源码下载:http://files.cnblogs.com/files/zhangbaochong/TextureDemo.zip

相关文章
相关标签/搜索