您的位置:首页 > 其它

DirectX (9) 纹理映射

2014-05-31 21:41 197 查看

引言

在前面几篇博文中,都没有使用纹理来对我们世界进行渲染,本次博文将向大家介绍,如果在DirectX9.0中,使用Shader来进行纹理映射。

纹理坐标系统

在DirectX中,使用的纹理坐标系统有两个轴向,分别称为u轴,和v轴。u轴从左到右依次增长,v轴从上到下依次增长。纹理坐标(u,v)定义了在纹理图片上的一个像素点,称为纹素。那么纹理坐标的范围取值该如何定义了?

在使用纹理坐标系统的时候,由于纹理的大小不总是相同的,所以,我们使用归一化的坐标来表示纹理的坐标范围,也就是说纹理坐标的范围从0到1。比如,如果我们想要选取纹理的中间像素,那么我们可以这样定义纹理坐标(0.5,0.5)。而如果,我们想要选取纹理左上角的像素,就可以这样定义纹理坐标(0,0)。同样的,如果我们想要获取纹理的右下角的纹素值,我们可以这样定义(1,1)。

纹理映射方法

为了能够让纹理图片正确的显示在我们的模型上,我们需要将纹理的坐标映射到模型上来。而在DirectX中,模型是以三角形的方式进行定义的。也就是说,我们该怎么样为三角形的三个顶点定义纹理坐标了?方法很简单,看看下面的图片就能够明白:



假如,我们有一个上面这样的四边形。我们想要将上面显示的纹理映射到这个四边形上去。我们知道要组合成这个四边形需要两个三角形,我用红色的线将他们划分了。

为了,能够将图片以上面的方式映射上去,我们就需要为这两个三角形,共四个顶点,定义不同的带有纹理的坐标。为此,我们需要先来确定一下带有纹理坐标的顶点格式,我采用如下的格式来定义顶点:

Vertex( x, y, z, u , v)

x和y,z定义了顶点的空间位置,而u和v定义了该顶点所拥有的纹理的坐标。所以,我们可以使用如下的方法来定义这四个顶点:

A(0,0,0, 0,0), B(128,0,0, 1,0) C(128,128,0, 1,1) D(0,128,0, 0,1)

好了,通过上面的方式,我们就能够正确的定义一个四边形的带有纹理坐标的顶点了。

在DirectX中创建纹理

在DirectX中,我们使用D3DXCreateTextureFromFile来创建一个纹理,它的函数原型如下所示:

HRESULT D3DXCreateTextureFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCSTR pSrcFile,
LPDIRECT3DTEXTURE9* ppTexture
);


在程序中,创建了纹理之后,我们需要将纹理传进到Shader中去,所以,我们需要在Shader中定义一个纹理变量用来接受传进来的纹理,我们使用如下的Shader代码来定义:

uniform extern texture gTex ;


定义了变量之后,我们就可以调用下面的方法,将纹理传递进去:

m_Fx->SetTexture(mhTex, mCreateTex)


其中m_Fx是你使用Shader文件创建的特效类,mhTex是gTex的句柄,mCreateTex就是你创建的纹理。

过滤器(Filters)

在3D空间中,纹理图的大小往往并不总是和定义的三角形同样的大小。也就是说,我们需要对纹理进行放大和缩小,也就是进行缩放操作。那么我们如何对纹理进行操作,才能够让纹理能够放大和缩小之后,不会变的混乱不堪了?

在这里,使用的就是各种采样方法,对纹理图进行采样,为了更好的效果可能还需要再使用滤波器进行过滤。在DirectX中,支持三种不同种类的采样方法。下面一一介绍他们。

首先给出这三种采样方式的名称,他们分别是:

点采样
双线性采样
三线性纹理滤波采样

我们知道,在上面定义了几个顶点的纹理坐标,但是对于一个三角形来说,它是一个平面,它需要将纹理整个的映射到这个平面上来。也就是说,如何通过这三个顶点的纹理坐标,来铺满整个平面? 读者,可能注意到这个问题,和我们以前讨论的如何通过顶点的颜色,然后将整个三角形进行着色的问题很相似。对头,这里,我们也是使用插值的方式来获取每一个像素点的纹理坐标,然后通过这个纹理坐标,采用不同的采样方式来获取纹理图中的像素值,使用这个采集的值来填充这个像素。

我们知道了如何进行纹理的插值(插值方法和前面介绍的颜色插值一致,这里不再赘述)之后,就要确定到底使用哪种采样方法来进行采样。下面将一一介绍不同的采样方法。

点采样

点采样,故名示意,就是使用我们进行插值后的纹理坐标来将它扩展到与纹理图相应的尺寸大小(还记的前面说过的,纹理坐标实际上是归一化的坐标),然后将这个变换后的纹理坐标进行取整,也就是截取小数部分,只保留整数部分,然后就使用这个整数的坐标来获取纹理图中对应的纹素值。

比如下面的数据:

我们纹理图的尺寸是128*128 ;

我们经过插值计算后的某个像素点的纹理坐标为(0,70,0.55)

那么我们将这个纹理坐标进行变化,使得纹理坐标的尺寸和纹理图的尺寸一致,即:

0.70 * 128 = 89.6, 0.55 * 128 = 70.4

再进行取整操作得到最后的纹理图上的坐标为(89, 70)

然后,我们就使用这样的坐标,来获取纹理图中第89列,第70行的那个像素的值,用这个值来填充我们计算的那个像素点的颜色。

读者可以看出,由于我们截取了小数部分,所以失去的部分的信息,这样的采样方法效果肯定是很不理想的。一种稍微改进点的方法就是保留小数部分,而将采取纹理图中相邻的两个像素的值,使用小数部分作为权值来进行采样。

拿上面的例子来说吧,我们计算后的保留小数的纹理图坐标为(89.6, 70.4),而取整之后的数据为(89,70)。

那么我们可以发现,这个像素实际上占用的空间是89列和90列这两个像素的位置,也就是说它有0.6的(89,70)位置像素值,有0.4的(90,70)的像素值,所以最后的像素值应该为:

0.6 * Texel(89,70) + 0.4 * Texel(90, 70)

通过这样的方式,我们能稍微的改进点采样方法的效果。

点采样方法效果很差,但是由于操作简单,所以效率会很高。

双线性采样

读者可能发现,我们上面讨论改进版的点采样方法时,故意没有考虑v坐标的跨度关系。也就是说,我只考虑了u坐标,在相邻两个坐标上的权值关系。所以,如果将v坐标上的权值关系也考虑进去,效果是否更加的逼真了呢?

的确,这就是所谓的双线性采样理论。通过在u和v两种维度上,都考虑权值关系,来获取最后的像素值。

还是拿上面的关系举例,很明显,这个纹理坐标牵涉到了四个像素,他们的坐标分别是(89,70), (90,70), (89,71)和(90,71)。

我们知道了它是和这四个像素点相关的,那么只要获取每一个像素点上的权值,我们自然就可以使用权值平均的方法来获取最后的像素值了。为了明确该纹理坐标,在这四个像素上所占有的权值,我们使用图示的方式来阐释:



我们就可以通过下面的公式来计算各个像素的权值,这个公式可以很容易的从上图中推导来:

(89,70) : (89.6 - 89) * (70.4 - 70) = 0.6 * 0.4 = 0.24 ;

(90,70) : (90 - 86.6) * (70.4 - 70) = 0.4 * 0.4 = 0.16 ;

(89,71) : (89.6 - 89) * (71 - 70.4) = 0.6 * 0.6 = 0.36 ;

(90,71) : (90 - 89.6) * (71 - 70.4) = 0.4 * 0.6 = 0.24 ;

我们将上面计算出来的权值相加,即0.24 + 0.16 + 0.36 + 0.24 = 1.0 ,也就是说完整的表述了这个像素值。

然后我们用上面的权值分别乘以每一个像素的值,来获取最后的像素:

0.24 * Texel(89, 70) + 0.16 * Texel(90, 70) + 0.36 * Texel(89, 71) + 0.24 * Texel(90, 71)

好了,通过上面的方法,我们就能够得到最后的像素值了,而且这个方法能够基本上完全保留纹理坐标的信息,所以效果十分的不错(之所以说基本上保留,是因为在进行插值计算的时候,使用浮点数,总是会存在一点误差,所以会损失一点信息)。很多游戏,都是采用这样的方法来进行纹理的缩放的。

Mipmap链

在讲解三线性纹理滤波采样之前,先来讲解下什么是Mipmap,以及使用Mipmap来做什么用途。

我们知道,在3D空间中,纹理图总是要被缩放的,而我们在原本纹理图上进行采样,并不总是那么可靠。比如说,纹理图的大小实际上是128*128的尺寸。而我们在3D程序中,我们仅仅需要一个4*4的纹理图就可以了。如果,我们在这个大图上,获取这个4*4的小图的话,操作复杂,而且效果不理想。所以,如果,我们能够预先使用这个大图,来创建一些尺寸较小的图,那么在进行采样的时候,我们可以选取,与需要的尺寸最接近的纹理图来进行采样。通过这样的方式,不仅能够提高效率,也能某种程度上改善效果。

Mipmap的作用就是这样的。在DirectX中,你加载纹理的时候,它总是为你创建了Mipmap链。如果你加载的是一个128*128的纹理图,那么它会为你创建一个64*64, 32*32, 16*16, 8*8, 4*4 , 2*2, 1*1的纹理图。

创建这些纹理图的方法,就是需要进行采样,同样的,它是使用前面介绍的双线性采样方法进行采样的。

当在3D空间中,某一个三角形需要一个纹理图的时候,我们先来判断,它最接近的纹理图是哪一个。比如说,它需要的实际上是50*50的纹理图,那么我们就会发现,使用64*64的纹理图,来进行采样,效果会更好。实际上,选择哪一个Mip等级,有很多不同的实现方法,我并不知道DirectX是使用哪种方式的,但是,它的原理无外乎就是选取最接近该纹理的纹理等级。

三次线性滤波采样

好了,在讲述完了上面的Mipmap之后,就可以来讲解如何实现三次线性滤波采样了。一般来说,三次线性滤波,已经是纹理滤波的极限了,没有办法做的比它更好了。实际上,这个滤波方式,就是结合了前面介绍的Mipmap和双线性采样理论来共同实现。

我们首先通过某种方法来获取最终的Mipmap等级,可能是通过面积计算,也可能是通过其他的方式来获取,而最终获取到的Mipmap等级值,会是像4.3这样的带有小数的值。这个小数的意思就是,我们将要使用Mipmap等级为4和5的这两个纹理来进行纹理采样,采样的方法就是使用双线性纹理滤波采样来进行。通过这样的方法,我们能够得到更加平滑的效果。

实现方式,将不会以代码的形式来提供给大家,如果读者感兴趣的话,可以自己写个软引擎,然后测试一下这个算法。但是不要期望,在软件引擎中大量的使用此种算法,这样的算法消耗将是非常巨大的。只能够少量的使用。

程序实例

为了能够在DirectX中,使用支持纹理坐标的顶点,我们先来创建如下的顶点格式:

//define the VertexPNT
struct VertexPNT
{
VertexPNT()
:_pos(0.0f, 0.0f, 0.0f),
_normal(0.0f, 0.0f, 0.0f),
_tex(0.0, 0.0)
{}
VertexPNT(float x, float y,float z , float nx, float ny, float nz, float tx, float ty)
:_pos(x,y,z),
_normal(nx, ny, nz),
_tex(tx, ty)
{}
VertexPNT(const D3DXVECTOR3& v, const D3DXVECTOR3& normal, const D3DXVECTOR2& tex)
:_pos(v),
_normal(normal),
_tex(tex)
{}
D3DXVECTOR3							_pos ;					//The position of the vertex
D3DXVECTOR3                         _normal ;				//The normal of this vertex
D3DXVECTOR2							_tex ;					//The texture of this vertex
static IDirect3DVertexDeclaration9* _vertexDecl ;			//The declaration of the vertex structure
};


定义了这个顶点结构之后,我们来为它创建顶点描述:

//Initialize VertexPNT
D3DVERTEXELEMENT9 _elementPNT[] =
{
{0,0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,0},
{0,12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
{0,24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,0},
D3DDECL_END()
};
HR(m_pDevice->CreateVertexDeclaration(_elementPNT, &VertexPNT::_vertexDecl));


好了,有了顶点之后,我们需要一个模型,在这里使用立方体模型,下面是产生立方体模型的代码:

void CubeDemo::genCube()
{
//Create the vertex buffer
HR(m_pDevice->CreateVertexBuffer(24 * sizeof(VertexPNT), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_pVertexBuffer, 0));

//Lock the buffer
VertexPNT* _vertex = NULL ;
HR(m_pVertexBuffer->Lock(0,0,(void**)&_vertex, 0));

//Front face
_vertex[0] = VertexPNT(-1,1,-1,-1,1,-1,0,0);
_vertex[1] = VertexPNT(1,1,-1,1,1,-1,1,0);
_vertex[2] = VertexPNT(1,-1,-1,1,-1,-1,1,1);
_vertex[3] = VertexPNT(-1,-1,-1,-1,-1,-1,0,1);

//Back face
_vertex[4] = VertexPNT(1,1,1,1,1,1,0,0);
_vertex[5] = VertexPNT(-1,1,1,-1,1,1,1,0);
_vertex[6] = VertexPNT(-1,-1,1,-1,-1,1,1,1);
_vertex[7] = VertexPNT(1,-1,1,1,-1,1,0,1);

//Right face
_vertex[8] = VertexPNT(1,1,-1,1,1,-1,0,0);
_vertex[9] = VertexPNT(1,1,1,1,1,1,1,0);
_vertex[10] = VertexPNT(1,-1,1,1,-1,1,1,1);
_vertex[11] = VertexPNT(1,-1,-1,1,-1,-1,0,1);

//Left face
_vertex[12] = VertexPNT(-1,1,1,-1,1,1,0,0);
_vertex[13] = VertexPNT(-1,1,-1,-1,1,-1,1,0);
_vertex[14] = VertexPNT(-1,-1,-1,-1,-1,-1,1,1);
_vertex[15] = VertexPNT(-1,-1,1,-1,-1,1,0,1);

//Top face
_vertex[16] = VertexPNT(-1,1,1,-1,1,1,0,0);
_vertex[17] = VertexPNT(1,1,1,1,1,1,1,0);
_vertex[18] = VertexPNT(1,1,-1,1,1,-1,1,1);
_vertex[19] = VertexPNT(-1,1,-1,-1,1,-1,0,1);

//Bottom face
_vertex[20] = VertexPNT(-1,-1,-1,-1,-1,-1,0,0);
_vertex[21] = VertexPNT(1,-1,-1,1,-1,-1,1,0);
_vertex[22] = VertexPNT(1,-1,1,1,-1,1,1,1);
_vertex[23] = VertexPNT(-1,-1,1,-1,-1,1,0,1);

HR(m_pVertexBuffer->Unlock());

//Create the index buffer
HR(m_pDevice->CreateIndexBuffer(36*sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,D3DPOOL_MANAGED, &m_pIndexBuffer,NULL));

//Lock the buffer
WORD * _indices = NULL ;
HR(m_pIndexBuffer->Lock(0,0,(void**)&_indices,0));

_indices[0] = 0 ; _indices[1] = 1 ; _indices[2] = 2 ;
_indices[3] = 0 ; _indices[4] = 2 ; _indices[5] = 3 ;

_indices[6] = 4 ; _indices[7] = 5 ; _indices[8] = 6 ;
_indices[9] = 4 ; _indices[10] = 6 ;_indices[11] = 7 ;

_indices[12] = 8 ; _indices[13] = 9 ; _indices[14] = 10;
_indices[15] = 8 ; _indices[16] = 10 ; _indices[17] = 11;

_indices[18] = 12 ; _indices[19] = 13 ; _indices[20] = 14 ;
_indices[21] = 12 ; _indices[22] = 14 ; _indices[23] = 15 ;

_indices[24] = 16 ; _indices[25] = 17 ; _indices[26] = 18 ;
_indices[27] = 16 ; _indices[28] = 18 ; _indices[29] = 19 ;

_indices[30] = 20 ; _indices[31] = 21 ; _indices[32] = 22 ;
_indices[33] = 20 ; _indices[34] = 22 ; _indices[35] = 23 ;

HR(m_pIndexBuffer->Unlock());

}


读者需要自己独立的定义自己的模型,上面定义的模型,只是一种方式而已,但是需要注意每一个顶点的绕序关系,并且注意,这里的顶点不再是8个,而是24个。这是因为,对于同一个立体空间坐标的顶点,在不同的面上,它的纹理坐标是不同的,而我们这里定义的顶点是包含纹理坐标的,如果两个顶点,即使他们的空间位置一样,但是他们的纹理坐标不一样的话,他们就不能作为一个相同的顶点来。所以,读者需要明确的区分这里的顶点和几何空间中的顶点的区别在哪里。

我们使用下面的代码来加载一个纹理图:

//Load the texture
HR(D3DXCreateTextureFromFile(m_pDevice,L"Sci_fic.bmp",&m_pTexture));


有了纹理图之后,我们来创建Shader接口:

void CubeDemo::createEffect()
{
//Create error buffer
ID3DXBuffer* _error = NULL ;
HR(D3DXCreateBuffer(128, &_error));

//Create the effect from a file
HR(D3DXCreateEffectFromFile(
m_pDevice,
L"Texture_Direction.fx",
NULL,
NULL,
D3DXSHADER_DEBUG,
NULL,
&m_pEffect,
&_error
));

//If error
if(_error)
{
MessageBoxA(NULL,(char*)_error->GetBufferPointer(),"Error", MB_OK);
return ;
}

//Get the technique handle
m_hTechnique = m_pEffect->GetTechniqueByName("TextureTech");
if(m_hTechnique == NULL)
{
MessageBox(NULL, L"GetTechnique error", L"Error", MB_OK);
return ;
}

//Get the gWVP handle
m_gWVP = m_pEffect->GetParameterByName(0, "gWVP") ;
if(m_gWVP == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gInverseTranspose
m_gInverseTranspose = m_pEffect->GetParameterByName(0,"gInverseTranspose");
if(m_gInverseTranspose == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gMaterial
m_gMaterial  = m_pEffect->GetParameterByName(0, "gMaterial");
if(m_gMaterial == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gLightColor
m_gLightColor = m_pEffect->GetParameterByName(0, "gLightColor");
if(m_gLightColor == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gLightVector
m_gLightVector = m_pEffect->GetParameterByName(0, "gLightVector");
if(m_gLightVector == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gWorld
m_gWorld = m_pEffect->GetParameterByName(0, "gWorld");
if(m_gWorld == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gEye
m_gEye = m_pEffect->GetParameterByName(0, "gEye");
if(m_gEye == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gPower
m_gPower = m_pEffect->GetParameterByName(0, "gPower");
if(m_gPower == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gSpecularLightColor
m_gSpecularLightColor = m_pEffect->GetParameterByName(0, "gSpecularLightColor");
if(m_gSpecularLightColor == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gSpecularMaterial
m_gSpecularMaterial = m_pEffect->GetParameterByName(0, "gSpecularMaterial");
if(m_gSpecularMaterial == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gAmbientLightColor
m_gAmbientLightColor = m_pEffect->GetParameterByName(0, "gAmbientLightColor");
if(m_gAmbientLightColor == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gAmbientMaterial
m_gAmbientMaterial = m_pEffect->GetParameterByName(0, "gAmbientMaterial");
if(m_gAmbientMaterial == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}

//Get the gTex
m_gTex = m_pEffect->GetParameterByName(0, "gTex");
if(m_gTex == NULL)
{
MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);
return ;
}
}


好了,有了Shader之后,我们就可以进行绘制了:

void CubeDemo::draw()
{
//Set the vertex declaration
HR(m_pDevice->SetVertexDeclaration(VertexPNT::_vertexDecl));

//Set the vertex buffer
HR(m_pDevice->SetStreamSource(0,m_pVertexBuffer,0,sizeof(VertexPNT)));

//Set the index buffer
HR(m_pDevice->SetIndices(m_pIndexBuffer));

//Create the world matrix
D3DXMATRIX _worldM ;
D3DXMatrixIdentity(&_worldM);

//Set the technique
HR(m_pEffect->SetTechnique(m_hTechnique));

//Set the gWVP matrix
D3DXHANDLE _hWVP = m_pEffect->GetParameterByName(0, "gWVP");
HR(m_pEffect->SetMatrix(_hWVP, &(_worldM* m_ViewMatrix* m_ProjMatrix)));

//Set the gWorld matrix
HR(m_pEffect->SetMatrix(m_gWorld, &_worldM));

//Set the gInverseTranspose
D3DXMatrixInverse(&_worldM,NULL, &_worldM);
D3DXMatrixTranspose(&_worldM, &_worldM);
HR(m_pEffect->SetMatrix(m_gInverseTranspose, &_worldM));

//Set the gPower
static float power = 5.0f;
HR(m_pEffect->SetValue(m_gPower,&power,sizeof(float)));

//Set the gMaterial
HR(m_pEffect->SetVector(m_gMaterial,&D3DXVECTOR4(0.5, 0.7, 0.0, 1.0)));

//Set the gLightColor
HR(m_pEffect->SetVector(m_gLightColor, &D3DXVECTOR4(0.8, 0.8, 0.8, 1.0)));

//Set the gLightVector
D3DXVECTOR4 lightVector ;
D3DXVec4Normalize(&lightVector, &D3DXVECTOR4(1,1,1,0));
HR(m_pEffect->SetVector(m_gLightVector, &lightVector));

//Set the gSpecularLightColor
HR(m_pEffect->SetValue(m_gSpecularLightColor, &D3DXCOLOR(1.0,1.0,1.0,1.0),  sizeof(D3DXCOLOR)));

//Set the gSpecularMaterial
HR(m_pEffect->SetValue(m_gSpecularMaterial, &D3DXCOLOR(0.2,0.2,0.2,1.0), sizeof(D3DXCOLOR)));

//Set the gAmbientLightColor
HR(m_pEffect->SetValue(m_gAmbientLightColor, &D3DXCOLOR(1.0,1.0,1.0,0.0),sizeof(D3DXCOLOR)));

//Set the gAmbientMaterial
HR(m_pEffect->SetValue(m_gAmbientMaterial, &D3DXCOLOR(0.5,0.5,0.5,0.0), sizeof(D3DXCOLOR)));

//Set the gTex
HR(m_pEffect->SetTexture(m_gTex, m_pTexture));

//Begin pass
UINT _pass = 0 ;
HR(m_pEffect->Begin(&_pass, 0));
HR(m_pEffect->BeginPass(0));

//Draw the primitive
drawCube();

//End pass
HR(m_pEffect->EndPass());
HR(m_pEffect->End());
}


下面是本次实例的Shader:

//---------------------------------------------------------------------------
// declaration	: Copyright (c), by XJ , 2014 . All right reserved .
// brief	: This file will define the Texture_Direction Shader.
// date		: 2014 / 5 / 29
//----------------------------------------------------------------------------
uniform float4x4 gWVP ;				//这个变量将会保存世界变换矩阵*相机变换矩阵*透视投影矩阵的积
//用这个矩阵,将点转化到裁剪空间中去

uniform float4x4 gInverseTranspose;		//这个变量将会保存世界变换矩阵的逆矩阵*转置矩阵,用来对法向量进行变换

uniform float4   gMaterial;			//这个变量用来保存顶点的材质属性,在本Demo中,将对所有的顶点使用相同的
//材质

uniform float4   gLightColor;			//这个变量将用来保存一个平行光的颜色

uniform float3   gLightVector;			//这个变量用来保存平行光的光照向量

uniform float4x4 gWorld;			//这个变量保存世界变换,用来对模型坐标进行变换,从而计算视向量

uniform float3   gEye;				//这个变量保存视点的位置,也就是相机的位置

uniform float    gPower	;			//这个变量将会控制反射光的衰减速度

uniform float4   gSpecularLightColor;		//反射光颜色

uniform float4   gSpecularMaterial;		//物体对反射光的材质属性

uniform float4   gAmbientLightColor;	        //环境光颜色

uniform float4   gAmbientMaterial;              //物体对环境光的材质属性

uniform extern texture gTex ;			//定义纹理

//定义采样器
sampler TexS = sampler_state
{
Texture = <gTex> ;
MinFilter = LINEAR ;
MagFilter = LINEAR ;
MipFilter = LINEAR ;
};

//定义顶点着色的输入结构体
struct OutputVS
{
float4 posH : POSITION0 ;
float2 tex0 : TEXCOORD0 ;
float3 normalW : TEXCOORD1 ;
float3 posW : TEXCOORD2 ;
};

OutputVS TextureVS(float3 posL: POSITION0, float3 normalL: NORMAL0, float2 tex:TEXCOORD0)
{
//清空OutputVS
OutputVS outputVS = (OutputVS) 0 ;

//对顶点的法向向量进行变换
normalL = normalize(normalL);
float3 normalW = mul(float4(normalL, 0.0f),
gInverseTranspose).xyz;
normalW = normalize(normalW);

//保存法相向量
outputVS.normalW = normalW ;

//保留顶点的世界坐标
float3 posW = mul(float4(posL, 1.0), gWorld).xyz ;
outputVS.posW = posW ;

//使用gWVP将世界坐标转化为裁剪坐标
outputVS.posH = mul(float4(posL, 1.0f), gWVP);

//保存纹理坐标
outputVS.tex0 = tex ;

//返回结果
return outputVS ;
}// end for Vertex Shader

float4 TexturePS(float2 tex0:TEXCOORD0, float3 normalW:TEXCOORD1, float3 posW:TEXCOORD2): COLOR
{
//--------------------------------------------------
// 光照计算
//--------------------------------------------------
//进行插值后,法相向量不再是归一化的,重新进行归一化操作
normalW = normalize(normalW);

//根据漫反射公式:
// Color = max(L * Normal, 0)*(LightColor*Material)
float s = max(dot(gLightVector,normalW), 0);
float3 diffuse = s*(gMaterial*gLightColor).rgb ;

//根据环境光公式:
// Color = AmbientColor * AmbientMaterial
float3 ambient = (gAmbientLightColor * gAmbientMaterial).rgb ;

//根据反射光公式:
// Color = pow((max(dot(r,v),0)),p) * (SpecularLightColor*SpecularMaterial)
float3 view = gEye - posW ;
view = normalize(view);
float3 ref = reflect(-gLightVector, normalW);
float t = pow(max(dot(ref,view),0),gPower);
float3 specular = t * (gSpecularLightColor * gSpecularMaterial).rgb ;

//--------------------------------------------------------------------
//  获取纹素
//--------------------------------------------------------------------
float3 pixel_color = tex2D(TexS, tex0).rgb;

diffuse = (diffuse + ambient) * pixel_color ;

return float4(diffuse + specular, gMaterial.a) ;
}// end for Pixel Shader

technique TextureTech
{
pass P0
{
vertexShader = compile vs_2_0 TextureVS();
pixelShader =  compile ps_2_0 TexturePS();
}
}


在这里,要说明下,为了能够在Shader中使用纹理,我们需要在Shader中定义采样器,也就是上面的这段代码:

//定义采样器
sampler TexS = sampler_state
{
Texture = <gTex> ;
MinFilter = LINEAR ;
MagFilter = LINEAR ;
MipFilter = LINEAR ;
};


这段代码,表示,这个采样器,是对gTex对应的纹理进行采样的。在Min(缩放)和Mag(放大)上面,都是使用LINEAR(双线性滤波器)来进行采样的。而为了能够使用Mipmap,我们同样需要为它定义滤波器,它也是一样的LINEAR线性滤波器。

有了这个采样器之后,我们就可以在像素着色器中使用它来获取纹素,并且与光照后的颜色进行融合。一般来说,我们认为,模型的纹理是模型固有颜色的一种,所以将它和原来的漫射光和环境光进行"*"运算。也就是如下的代码:

//--------------------------------------------------------------------
//  获取纹素
//--------------------------------------------------------------------
float3 pixel_color = tex2D(TexS, tex0).rgb;

diffuse = (diffuse + ambient) * pixel_color ;


好了,下图是本次实例的最终结果:



好了,今天的文章又结束了!明天再见!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: