引言

在前面几篇博文中,都没有使用纹理来对我们世界进行渲染,本次博文将向大家介绍,如果在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 vertexD3DXVECTOR3                         _normal ;               //The normal of this vertexD3DXVECTOR2                          _tex ;                  //The texture of this vertexstatic IDirect3DVertexDeclaration9* _vertexDecl ;           //The declaration of the vertex structure
};

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

//Initialize VertexPNTD3DVERTEXELEMENT9 _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 bufferHR(m_pDevice->CreateVertexBuffer(24 * sizeof(VertexPNT), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_pVertexBuffer, 0));//Lock the bufferVertexPNT* _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 bufferHR(m_pDevice->CreateIndexBuffer(36*sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,D3DPOOL_MANAGED, &m_pIndexBuffer,NULL));//Lock the bufferWORD * _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 textureHR(D3DXCreateTextureFromFile(m_pDevice,L"Sci_fic.bmp",&m_pTexture));

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

void CubeDemo::createEffect()
{//Create error bufferID3DXBuffer* _error = NULL ;HR(D3DXCreateBuffer(128, &_error));//Create the effect from a fileHR(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 handlem_hTechnique = m_pEffect->GetTechniqueByName("TextureTech");if(m_hTechnique == NULL){MessageBox(NULL, L"GetTechnique error", L"Error", MB_OK);return ;}//Get the gWVP handlem_gWVP = m_pEffect->GetParameterByName(0, "gWVP") ;if(m_gWVP == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gInverseTransposem_gInverseTranspose = m_pEffect->GetParameterByName(0,"gInverseTranspose");if(m_gInverseTranspose == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gMaterialm_gMaterial  = m_pEffect->GetParameterByName(0, "gMaterial");if(m_gMaterial == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gLightColorm_gLightColor = m_pEffect->GetParameterByName(0, "gLightColor");if(m_gLightColor == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gLightVectorm_gLightVector = m_pEffect->GetParameterByName(0, "gLightVector");if(m_gLightVector == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gWorldm_gWorld = m_pEffect->GetParameterByName(0, "gWorld");if(m_gWorld == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gEyem_gEye = m_pEffect->GetParameterByName(0, "gEye");if(m_gEye == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gPowerm_gPower = m_pEffect->GetParameterByName(0, "gPower");if(m_gPower == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gSpecularLightColorm_gSpecularLightColor = m_pEffect->GetParameterByName(0, "gSpecularLightColor");if(m_gSpecularLightColor == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gSpecularMaterialm_gSpecularMaterial = m_pEffect->GetParameterByName(0, "gSpecularMaterial");if(m_gSpecularMaterial == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gAmbientLightColorm_gAmbientLightColor = m_pEffect->GetParameterByName(0, "gAmbientLightColor");if(m_gAmbientLightColor == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gAmbientMaterialm_gAmbientMaterial = m_pEffect->GetParameterByName(0, "gAmbientMaterial");if(m_gAmbientMaterial == NULL){MessageBox(NULL, L"GetParameterByName error", L"Error", MB_OK);return ;}//Get the gTexm_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 declarationHR(m_pDevice->SetVertexDeclaration(VertexPNT::_vertexDecl));//Set the vertex bufferHR(m_pDevice->SetStreamSource(0,m_pVertexBuffer,0,sizeof(VertexPNT)));//Set the index bufferHR(m_pDevice->SetIndices(m_pIndexBuffer));//Create the world matrixD3DXMATRIX _worldM ;D3DXMatrixIdentity(&_worldM);//Set the techniqueHR(m_pEffect->SetTechnique(m_hTechnique));//Set the gWVP matrixD3DXHANDLE _hWVP = m_pEffect->GetParameterByName(0, "gWVP");HR(m_pEffect->SetMatrix(_hWVP, &(_worldM* m_ViewMatrix* m_ProjMatrix)));//Set the gWorld matrixHR(m_pEffect->SetMatrix(m_gWorld, &_worldM));//Set the gInverseTransposeD3DXMatrixInverse(&_worldM,NULL, &_worldM);D3DXMatrixTranspose(&_worldM, &_worldM);HR(m_pEffect->SetMatrix(m_gInverseTranspose, &_worldM));//Set the gPowerstatic float power = 5.0f;HR(m_pEffect->SetValue(m_gPower,&power,sizeof(float)));//Set the gMaterialHR(m_pEffect->SetVector(m_gMaterial,&D3DXVECTOR4(0.5, 0.7, 0.0, 1.0)));//Set the gLightColorHR(m_pEffect->SetVector(m_gLightColor, &D3DXVECTOR4(0.8, 0.8, 0.8, 1.0)));//Set the gLightVectorD3DXVECTOR4 lightVector ;D3DXVec4Normalize(&lightVector, &D3DXVECTOR4(1,1,1,0));HR(m_pEffect->SetVector(m_gLightVector, &lightVector));//Set the gSpecularLightColorHR(m_pEffect->SetValue(m_gSpecularLightColor, &D3DXCOLOR(1.0,1.0,1.0,1.0),  sizeof(D3DXCOLOR)));//Set the gSpecularMaterialHR(m_pEffect->SetValue(m_gSpecularMaterial, &D3DXCOLOR(0.2,0.2,0.2,1.0), sizeof(D3DXCOLOR)));//Set the gAmbientLightColorHR(m_pEffect->SetValue(m_gAmbientLightColor, &D3DXCOLOR(1.0,1.0,1.0,0.0),sizeof(D3DXCOLOR)));//Set the gAmbientMaterialHR(m_pEffect->SetValue(m_gAmbientMaterial, &D3DXCOLOR(0.5,0.5,0.5,0.0), sizeof(D3DXCOLOR)));//Set the gTexHR(m_pEffect->SetTexture(m_gTex, m_pTexture));//Begin passUINT _pass = 0 ;HR(m_pEffect->Begin(&_pass, 0));HR(m_pEffect->BeginPass(0));//Draw the primitivedrawCube();//End passHR(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)
{//清空OutputVSOutputVS 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 Shaderfloat4 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 * AmbientMaterialfloat3 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 Shadertechnique 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 ;

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

好了,今天的文章又结束了!明天再见!!!

DirectX (9) 纹理映射相关推荐

  1. 【Visual C++】游戏开发笔记四十一 浅墨DirectX教程之九 为三维世界添彩:纹理映射技术(一)...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8523341 作者:毛星云(浅墨 ...

  2. 【Visual C++】游戏开发笔记四十三 浅墨DirectX教程十一 为三维世界添彩:纹理映射技术(二)...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 作者:毛星云(浅墨)    邮箱: happylifemxy@163.com 本篇文章里,我们首先对Direct3D之中固定功能流水线中的 ...

  3. 【Visual C++】游戏开发笔记四十三 浅墨DirectX教程十一 为三维世界添彩 纹理映射技术 二

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  4. DirectX 基础学习系列5 纹理映射

    1 纹理坐标 类似BMP图像坐标系,左上为原点 纹理坐标为了规范化,范围限定在[0,1]之间,使用纹理的时候,需要修改顶点结构 struct ColorVetex { float x, y,z; fl ...

  5. DirectX (13) 粒子系统

    笔者:i_dovelemon 资源:CSDN 日期:2014 / 10 / 16 主题:Point Sprite, Particle System 介绍 在游戏中.非常多的绚丽,灿烂的特效,都能够使用 ...

  6. 整理了一下浅墨大神的Visual C++/DirectX 9.0c的游戏开发手记

    还是很棒的博客,不过没有一个目录,所以自己做了一个山寨目录在这里,便于随时查找.前面31期从略. [Visual C++]游戏开发笔记三十二 浅墨DirectX提高班之一 DirectX大局观认知篇: ...

  7. 【Visual C++】游戏开发笔记四十七 浅墨DirectX教程十五 翱翔于三维世界 摄像机的实现

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 本系列文章由zhm ...

  8. directX学习系列8 颜色融合(转)

    1, Multipass(多通道)    将一个任务划分成几个阶段,由多个pass处理不同阶段,后续pass总是处理前一个pass的结果.例如复杂的光照方程可以分成几个pass来计算.    用不同的 ...

  9. DirectX 9.0c游戏开发手记之RPG编程自学日志之11: 题外话

            本文由哈利_蜘蛛侠原创,转载请注明出处!有问题请联系2024958085@qq.com 这一期是特别篇,不讲具体的编程知识了.在讲述内容之前,我首先要恭喜你--对,没错!就是你!坐在显 ...

最新文章

  1. 安装wamp时出现httpd.exe无法找到组件MSVCR100.dll的解决办法
  2. svn使用经验---不断总结
  3. Unity3D 人形血条制作小知识
  4. php常见问题及其解决方案,PHP开源开发框架ZendFramework使用中常见问题说明及解决方案...
  5. vmware VCenter6.7以上版本导出ova
  6. GroupCoordinator介绍
  7. android 无线接口 泛收,Android下的Java之interface接口泛型 动态获取泛型的类型
  8. 后台业务管理系统原型模板/在线教育后台管理系统/客服系统/财务管理/用户管理/订单管理/教育业务后台管理/课程管理/教师管理/活动管理/文章管理/Axure高保真在线教育行业原型/Axure后台管理
  9. Mac(不限于)中几个有内涵的工具
  10. linux下node-webkit安装vlc插件
  11. c语言三角波的mif文件,EDA课程设计报告-正弦波信号发生器的设计.doc
  12. 算法题_寻找最大连通区域
  13. 如歌芳华,编剧柯伊玟获奖后畅谈从影历程
  14. Axure9网易云低保真原型设计
  15. Golang big.int类型转int
  16. C++11 for(auto x : s)statment 和 for(auto amp; x:s)statment
  17. 数据结构与算法(Python版)十六:有序表抽象数据类型及Python实现
  18. 酷q服务器信息获取,酷Q获取加群QQ等级的解决方案
  19. 本次秋招最差面试体验给到华为
  20. ESP32刷入MicroPython固件

热门文章

  1. iOS开发-常用第三方开源框架
  2. 火山PC写出内置资源文件
  3. 2021年新西兰经济发展研究报告
  4. 内丘计算机学校,内丘学校食堂打卡机
  5. 驾驶员考试计算机辅助教学系统藏汉又(4),学科考试题库及维,汉模拟考试系统...
  6. gwb-crypto-1
  7. 《系统架构设计师教程》 第一章:绪论
  8. 【Linux】冯诺依曼体系结构与操作系统概念理解
  9. mybatis动态sql及分页
  10. sscom 中文显示 乱码_SSM框架:解决后台传数据到前台中文乱码问题,使用@ResponseBody返回json 中文乱码 Web程序 - 贪吃蛇学院-专业IT技术平台...