高级纹理映射知识汇总


多阶层纹理的基本概念

纹理拥有一个surface表面和格式大小等数据,后台缓存也有一个surface,是可以从后台缓存获取surface拷贝到texture的surface的但是有点影响效率。

这里多阶层纹理融合是指对单个物体多边形顶点像素上设置多个阶层纹理时候的融合操作,最终得到物体多边形顶点像素上的颜色。而

一般的融合指着色阶段的网格物体颜色和后台缓存的融合,该blend融合也可以设置融合操作和基于颜色还是alpha融合。

多通道纹理
(多次渲染, 每次设置一个纹理)已经不用了,现在DX6后都是用单通道多阶层Stage纹理(一次渲染可以将多个纹理融合到一个多边形顶点上),老的多通道纹理是可以转换到多阶层纹理的。

多阶段纹理设置的才启用,不设置的不启用,如果要禁用一个阶段纹理设置它的颜色操作为COLOROP_DISABLE即可。

Device->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
Device->SetTextureStageState(i, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

每个阶段纹理都有自己的对应纹理指针对象,和纹理采用的顶点坐标,还有纹理过滤的采样状态
Device->SetTexture(i, pTexObj);
 Device->SetTextureStageState(i, D3DTSS_TEXCOORDINDEX, 0); 

 Device->SetSamplerState(
i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(
i, 
 D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// 
MipMapTexture 
多幅渐进纹理,在CreateTextureFromFile和CreateTextureFromFileEx时候就会创建,这里选择纹理采用线性插值
 Device->SetSamplerState(
i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

每个阶段纹理都有自己的两个参数纹理和操作,两个参数arg1和arg2, arg1是当前stage的纹理,arg2是第0层纹理或者是上一层融合的结果纹理,操作有两种分别是alpha操作和颜色操作,两个参数arg1和arg2 会根据操作进行设置值,操作就是指在两个颜色之间进行的从操作:color =  arg1 opr arg2, opr是D3DTOP_ADD就是加,不会像物体和后台缓存融合一样先进行分量乘再相加。
pd3dDevice->SetTextureStageState(i, D3DTSS_XXXARG1, D3DTA_XXX);
pd3dDevice->SetTextureStageState(i, D3DTSS_XXXARG2, D3DTA_XXX);
pd3dDevice->SetTextureStageState(i, D3DTSS_XXXOP, D3DTOP_XXX);
阶段状态前缀:D3DTSS_ 前缀是Texture stage state, 有D3DTSS_COLOROP和D3DTSS_ALPHAOP以及他们相同的操作值,有 D3DTSS_COLORARG1相同以及他们相同的操作值

状态的值类型为:D3DTOP_ 前缀是Operation。
或者状态的值类型为:D3DTA_  前缀是Texture Argument。

参数一般是两元组合,如果需要三元组合操作那么需要:
 D3DTSS_COLORARG0      = 26, /* D3DTA_* third arg for triadic ops */
 D3DTSS_ALPHAARG0      = 27, /* D3DTA_* third arg for triadic ops */

检测多阶层纹理融合阶层数:
D3DCAPS9 pCaps;
 Device->GetDeviceCaps(&pCaps);
 if(pCaps.MaxTextureBlendStages <= 1)
  return false;

纹理阶层的用法

1.可以用纹理阶层指定获取alpha值,默认是D3DTA_TEXTURE的

// 可以用当前纹理阶段选择alpha值,可以选择来自纹理或者来自漫反射光
 Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE/*D3DTA_DIFFUSE*/);
 Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
 Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
// 绘制物体
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);

2.用于指定纹理和光照混合(一般是第0阶层纹理),默认是采用顶点纹理值和光照相乘的


Device->SetTexture(0, BackTex);
 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); 

// 纹理阶段设置
 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
 
// 用D3DTOP_ADD可以实现强光照效果,白光
 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
 //
 
颜色参数来自顶点光照漫反射颜色,如果没有开启光照那么arg1 x arg2颜色也不会有问题 ,会得到arg1的颜色,
 //
 
或者很多多阶层融合纹理都是第0阶层3DTSS_COLOROP值取D3DTOP_SELECTARG1就可以了。
 //
 可用3DTSS_COLOROP值取D3DTOP_SELECTARG1来避免光照影响,也就是顶点颜色融合不采用光照。
 Device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
 Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
// 纹理采样设置
 Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

// 光照开启,使得多边形顶点的颜色可以由纹理颜色和光照颜色决定
const D3DXCOLOR      WHITE( D3DCOLOR_XRGB(255, 255, 255) );
 const D3DXCOLOR     YELLOW( D3DCOLOR_XRGB(255, 255,   0) );
 D3DXVECTOR3 lightDir(0.0f, 0.0f, 1.0f);
 D3DXCOLOR lightColor = D3DCOLOR_XRGB(255, 255, 255);
 D3DLIGHT9 light = d3d::InitDirectionalLight(&lightDir, &lightColor);
 D3DMATERIAL9 matrial = d3d::InitMtrl(YELLOW, YELLOW, YELLOW, BLACK, 2.0f);
 // 使用这样的环境光颜色,会导致绘制异常
 //Device->SetRenderState(D3DRS_AMBIENT, 0x00808080);

/*可指定漫反射光的来源,可以来自材质D3DMCS_MATERIAL,顶点漫反射颜色 D3DMCS_COLOR1,顶点镜面反射颜色 D3DMCS_COLOR2
 
 默认是来自于D3DMCS_COLOR1,一般默认就好了 
*/
 Device->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE,
 
D3DMCS_MATERIAL);

 // 开启光照下面4个都要设置
 Device->SetRenderState(D3DRS_LIGHTING, true);// 默认为true,不需要记得关闭
 Device->SetMaterial(&matrial);
 Device->SetLight(0, &light);
 Device->LightEnable(0, TRUE);

// 额外的光照设置,这里不用也是可以的
 /*Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
 Device->SetRenderState(D3DRS_SPECULARENABLE, false);*/

3.黑暗纹理用光照贴图模拟像素光照

因为光源很接近物体时候会导致顶点没有光照而使得三角形内没有光照,所以需要基于像素的光照因为逐像素计算光照计算量太大,所以需要用光照贴图来代替光照。
// 背景纹理
pd3dDevice->SetTexture(0, g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);

// 光照纹理和背景纹理,进行D3DTOP_MODULATE分量乘法计算,得到黑暗纹理
pd3dDevice->SetTexture(1, g_dark_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);



4. 黑暗纹理动画,通过不同的分量相乘作为结果输出,也可以用不同的纹理算法和状态间切换实现特效

// 1.切换纹理阶段融合的算法
static double interval = 0.0f;
  interval += 0.0015f;
  char szBuffer[256];
  sprintf(szBuffer, “interval: %1.4f\n”, interval);
  OutputDebugString(szBuffer);
  if(interval < 0.5f)
   Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
  else if(interval > 0.5f && interval < 1.0f)
   // D3DTOP_MODULATE2X是分量相乘后得到的颜色左移1位作为最终颜色
   Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE2X);
  else if(interval > 1.0f && interval < 1.5f)
   // D3DTOP_MODULATE2X是分量相乘后得到的颜色左移2位作为最终颜色
   Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE4X);
  if(interval < 0.0f || interval > 1.5f)
   interval = timeDelta;

5.实现黑暗环境中灯光开灯闪烁效果

这个过程可以描述为”(基础贴图 x 顶点的漫反射颜色) x 黑暗贴图”。可以从没有光照中切换到有光照中出现灯光,再通过设置1阶段和0阶段纹理的融合方式从分量乘法变为加法实现全部照明,来模拟开灯效果。

// 1.设置阶段纹理
Device->SetTexture(0, BackTex);
 // D3DTOP_MODULATE分量乘法
 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
 Device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
 Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);

 Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);


 // set color blend operation and texture coordinate index for texture stage 1
 Device->SetTexture(1, LightTex);
 // D3DTOP_MODULATE分量乘法
 Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
 Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
 Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
 Device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
 Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);

 Device->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
 Device->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

// 2. 材质设置和光照启用
D3DXVECTOR3 lightDir(0.0f, 0.0f, 1.0f);
 D3DXCOLOR lightColor = D3DCOLOR_XRGB(128, 128, 128);
 D3DLIGHT9 light = d3d::InitDirectionalLight(&lightDir, &lightColor);
 D3DMATERIAL9 matrial = d3d::InitMtrl(WHITE, WHITE * 0.7f, BLACK, BLACK, 0.0f);

Device->SetRenderState(D3DRS_LIGHTING, true);
 Device->SetMaterial(&matrial);
 Device->SetLight(0, &light);
 Device->LightEnable(0, TRUE);

// 3.Display每帧渲染函数中切换有光照和没光照
static int nFrameTime = 0;
  if( nFrameTime < 1000 )
  {
   Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
// 光照黑暗效果

   Device->SetRenderState(D3DRS_LIGHTING, TRUE);
  }
  else if (nFrameTime < 2000)
  {
    // 光照黑暗效果
   Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
   Device->SetRenderState(D3DRS_LIGHTING, FALSE);
  }
  else if (nFrameTime < 3000)
  {
    // 光照发光效果
 
 
 Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
  }
  nFrameTime++;
  if( nFrameTime >= 3000)
  {
   nFrameTime = 0;
  }

6.细节纹理

细节纹理,在第后面阶层纹理,用
D3DTOP_ADDSIGNED纹理操作算法。
 /*D3DTOP_ADDSIGNED
 color rgba = arg1 + arg2 – 0.5
  Add the components of the arguments with a – 0.5 bias,
  making the effective range of values from – 0.5 through 0.5. */
它将参数的每个成分相加后再减去偏移量0.5,在细节贴图中较亮的灰色纹理元素会使基础贴图变得更亮,而较暗的灰色纹理元素会使基础贴图变得更暗。由此可使物体呈现出粗糙的表面,从而使之看上去更为真实。

Device->SetTexture(0, BackTex);
 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);

 Device->SetTexture(1, DetailTex);
 Device->SetTextureStageState(1,D3DTSS_TEXCOORDINDEX, 0);
 Device->SetTextureStageState(1,D3DTSS_COLORARG1, D3DTA_TEXTURE);
 Device->SetTextureStageState(1,D3DTSS_COLORARG2, D3DTA_CURRENT);

 if( FAILED( Device->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED )) )
 {
  Device->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE2X );
 }

7.纹理阶层的Alpha混合操作

// alpha来源设置

如果要从一张纹理中获取Alpha值,应将D3DTA_TEXTURE作为Alpha参数。
如果要使用来自顶点中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_COLOR1(这也是默认状态)。

如果要使用来自材质中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_MATERIAL。

如果未用SetRenderState()设置D3DRS_DIFFUSEMATERIALSOURCE参数,则从默认来源(即顶点)获取漫反射颜色。


//设置纹理颜色和光照顶点颜色进行融合

 Device->SetTexture( 0, g_pTexture );

 Device->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );

 Device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );

 Device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

 Device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );

// 选择纹理alpha作为第0阶层纹理的颜色,这里也就是多边形顶点颜色的alpha值

 Device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );

 Device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );


 //Alpha混合设置, 设置混合系数

 pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   true );

 pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );

 pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );


Texture Wrapping纹理包装

影响纹理光栅化时候的插值,说明究竟是默认直线插值,还是沿着UV进行曲线插值计算像素纹理坐标:
// D3DRS_WRAP0是指第0阶层纹理,D3DWRAPCOORD_0就是D3DWRAP_U,D3DWRAPCOORD_1是D3DWRAP_V方向
 // 这里是指对第0层纹理采用UV方向都进行曲线空间包装类型的插值,而不是两点间直线插值。
 // 要禁用纹理包装类型的插值,那么SetRenderState( D3DRS_WRAP0, 0)即可,默认也是0值的。
 // 只有空间物体环境纹理中,才会用到纹理包装,否则平面内使用纹理包装会导致着色异常。
 //Device->SetRenderState( D3DRS_WRAP0, D3DWRAPCOORD_0 /*| D3DWRAPCOORD_1 */);

CubeMapTexture立方体纹理映射和球形纹理映射实现环境纹理

自动生成纹理坐标和自动纹理坐标变换,用于立方体纹理映射和球形纹理映射。
和普通纹理的区别就是使用自动纹理坐标,和指定uv纹理坐标用顶点法线作为uv坐标,还是球形的。

一般应用:

用于天空盒贴图。

用于物体反射周围环境颜色。

球形环境纹理

基础设置:

Device->SetTexture(0, BackTex);
 Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
 Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
 Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
 Device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );

1)利用摄影机空间顶点位置生成纹理坐标,自动生成纹理坐标不需要顶点中包含纹理坐标信息
 // 自动生成纹理坐标只适合在一些特殊效果上,所以大多数情况下顶点还是要包含纹理坐标信息的
 // 自动生成,一般情况下只用在环境纹理上
 Device->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION/*D3DTSS_TCI_SPHEREMAP*/);
 // 控制纹理坐标自动生成的格式标识,一般是二维的
 Device->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );

2)纹理坐标变换只能应用于自动生成的纹理坐标中,变换矩阵和顶点的矩阵一样
 D3DXMATRIXA16 matTex, matScale, matTrans;
 D3DXMatrixIdentity(&matTex);
 D3DXMatrixScaling(&matScale, 0.5f, -0.5f, 1.0f);
 D3DXMatrixTranslation(&matTrans, 0.5f, 0.5f, 1.0f);
 matTex = matTex * matScale * matTrans;
// 坐标系和D3DTS_WORLD,D3DTS_VIEW,D3DTS_PROJECTION一样,纹理坐标有D3DTS_TEXTURE0~D3DTS_TEXTURE7
 Device->SetTransform( D3DTS_TEXTURE0, &matTex );

//设置球形环境映射纹理
 pd3dDevice->SetTexture( 0, g_pEnvTexture);

 //设置纹理坐标自动生成
 pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );

 //设计纹理阶段混合状态
 pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);

3)自动纹理坐标生成应用最为广泛的环境纹理映
环境纹理映射,就是将周围环境在光滑三维物体上的映像,用一幅贴图描绘出来,然后映射到物体表面,其实就是简单的映射,唯一不同就是用了自动生成纹理坐标,且这里没有使用自动生成纹理坐标转换。
//设置球形环境映射纹理
 pd3dDevice->SetTexture( 0, g_pEnvTexture);

 //设置纹理坐标自动生成
 pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );

 //设计纹理阶段混合状态
 pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
// 用分量乘法,将物体表面顶点漫反射和当前的环境纹理融合
 pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
 pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);

CubeMapTexture立方体纹理

立方体纹理是一种特殊的纹理技术,它用6幅二维纹理图像构成一个以原点为中心的纹理立方体。对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹理单元都表示从原点所看到的纹理立方体上的图像。

它有6个面,每个面都是一个二维图像,每个二维图像用一个x,-x, y, -y, z, -z来表示。

OGL中指定六个面的方式:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA,  
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image1);  


glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA,  
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image4);  


glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA,  
 imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image2);  


glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA,  
 imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image5);  


glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA,  
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image3);  


glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA,  
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image6);  
指定立方体纹理对象的参数:
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,  
                GL_REPEAT);  
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,  
                GL_REPEAT);  
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R,  
                GL_REPEAT);  
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,  
                GL_NEAREST);  
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,  
                GL_NEAREST);
纹理纹素的获取算法
          正如前面所述,对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹理单元都表示从原点所看到的纹理立方体上的图像。参见实现代码来自mesa3d里面,源文件路径为Mesa-8.0.4\src\mesa\swrast\s_texfilter.c
应用立体纹理时候,还需要指定:
使用vertex的法线作为uv坐标;   (GL_NORMAL_MAP)

用于天空盒贴图。

使用法线和相机到vertex的向量,推导出入射光向量作为uv坐标 (GL_REFLECTION_MAP)

用于物体反射周围环境颜色。

然后自动或者手动shader计算立体纹理uv坐标,然后渲染物体就可以了。


bumpTexture凹凸纹理映射

普通的纹理映射只能模拟比较平滑的三维物体表面,难以显示表面高低起伏、凹凸不平的效果。凹凸纹理映射能够通过一张表示物体表面凹凸程度的高度图(称为凹 凸纹理),对另一张表示物体表面环境映射的纹理图的纹理坐标进行相应的干扰,经过干扰的纹理坐标将应用于环境映射,从而产生凹凸不平的显示效果。凹凸纹理 映射通常由三张纹理映射图组成,第一张纹理图表示物体表面原始纹理颜色,第二张凹凸纹理图表示物体表面凹凸的高度起伏值,用来对下一张环境纹理图坐标进行 干扰,第三张纹理图表示周围镜面反射或漫反射光照的环境光照映射图。

1) 检测设备是否支持凹凸纹理
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
 D3DFORMAT BackBufferFormat, bool bWindowed,
 void* pUserContext )
{
 //检查后台缓冲区格式是否支持Alpha混合等操作(post pixel blending operations)
 IDirect3D9* pD3D = d3d::d3d9;
 if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
  AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING,
  D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
  return false;

 //检查当前设备是否支持凹凸纹理
 if(0 == (pCaps->TextureOpCaps&(D3DTEXOPCAPS_BUMPENVMAP|D3DTEXOPCAPS_BUMPENVMAPLUMINANCE)))
  return false;

 //检查当前设备是否支持3层纹理混合
 if(pCaps->MaxTextureBlendStages < 3)
  return false;

 //检查当前设备是否支持设置的凹凸纹理像素格式
 if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
  AdapterFormat, D3DUSAGE_QUERY_LEGACYBUMPMAP,  
  D3DRTYPE_TEXTURE, D3DFMT_X8L8V8U8 ) ) )
  return false;

 return true;
}
2) 凹凸纹理的生成

Direct3D的凹凸纹理被用来表示物体表面相邻像素的高度差,它的每个纹理元素由表示水平相邻像素高度差的Du、表示垂直相邻像素高度差的Dv以及表示该点亮度的L组成(某些凹凸纹理像素格式可以不包含L)。下表列出了Direct3D支持的凹凸纹理像素格式:

凹凸纹理像素格式 说明
D3DFMT_V8U8 每个像素由16位整数表示,分别由8位整数表示Du和Dv
D3DFMT_L6V5U5 每个像素由16位整数表示,6位整数表示L,分别由5位整数表示Du和Dv
D3DFMT_X8L8V8U8 每个像素由32位整数表示,包括8位保留位、8位L、8位Du、8位Dv
D3DFMT_V16U16 每个像素由32位整数表示,分别由16位整数表示Du和Dv
D3DFMT_Q8W8V8U8 每个像素由32位整数表示,分别由8位整数表示Q、W、V、U
D3DFMT_CxV8U8 压缩像素格式,每个像素由16位整数表示,即8位Du和8位Dv,另外C = sqrt(1 – Du2 – Dv2  )

通常情况下,可以载入一张表示物体表面图像高度的纹理图,通过计算高度图水平相邻和垂直相邻元素的高度差来生成凹凸纹理,也可以通过程序生成凹凸纹理,这里根据高度纹理图,用程序来生成凹凸纹理,代码如下:

HRESULT CreateBumpTexture(IDirect3DDevice9* device, IDirect3DTexture9* pHeightMapTexture, IDirect3DTexture9* pBump_map_texture)
{
	HRESULT hr;
	D3DSURFACE_DESC surface_desc;
	// pHeightMapTexture是高度纹理图,每个像素存放的是高度,虽然大小是4个字节,但是只用一个字节也就是大小为[0,255]
	// 从纹理的表面数据中获取纹理的具体数值
	pHeightMapTexture->GetLevelDesc(0, &surface_desc);
	// 创建一个凹凸纹理图,凹凸纹理的每个像素存放的是D3DFMT_X8L8V8U8,
	// 值为:预留值X8、亮度值L8、相邻上高度减去下高度的值V8、左高度减去右高度的值U8
	device->CreateTexture(surface_desc.Width, surface_desc.Height, 1, 0, D3DFMT_X8L8V8U8, D3DPOOL_MANAGED,
		&pBump_map_texture, NULL);
	D3DLOCKED_RECT locked_rect;
	pHeightMapTexture->LockRect(0, &locked_rect, NULL, 0);
	// 一行的宽度
	DWORD src_pitch   = (DWORD) locked_rect.Pitch;
	BYTE* src_row_top = (BYTE*) locked_rect.pBits;
	BYTE* src_row_cur = src_row_top;
	BYTE* src_row_bot = src_row_top + src_pitch * (surface_desc.Height - 1);
	pBump_map_texture->LockRect(0, &locked_rect, NULL, 0);
	DWORD dest_pitch   = (DWORD) locked_rect.Pitch;
	BYTE* dest_row_top = (BYTE*) locked_rect.pBits;
	BYTE* dest_row_cur = dest_row_top;
	// iterate through all lines
	for(DWORD y = 0; y < surface_desc.Height; y++)
	{
		// 源像素
		BYTE* src_pixel_cur;
		// 源上一行的像素
		BYTE* src_pixel_up;
		// 源下一行的像素
		BYTE* src_pixel_below;		
		// 目标像素
		BYTE* dest_pixel;
		// 取当前行做为源像素
		src_pixel_cur = src_row_cur;
		// 当y为0第一行时候,源像素的上一行为最后一行像素
		if(y == 0)
		{
			src_pixel_up = src_row_bot;
		}
		// 否则上一行的像素等于当前行的像素减去一行的宽度
		else
		{
		    src_pixel_up = src_row_cur - src_pitch;
		}
		// 最末一行的下一行像素,是第一行像素
		if(y == surface_desc.Height - 1)
		{
			src_pixel_below = src_row_top;
		}
		// 否则下一行像素,等于当前行加上行宽
		else
		{
			src_pixel_below = src_row_cur + src_pitch;
		}
		// 目标行等于当前目标行
		dest_pixel = dest_row_cur;
		// iterate through all columns in current line
		for(DWORD x = 0; x < surface_desc.Width; x++)
		{
			BYTE src_pixel_left, src_pixel_right;
			if(x == 0)
				// 第0列,左边一个像素等于最后一个像素,4个字节等于D3DFMT_X8L8V8U8大小
				src_pixel_left = *(src_row_cur + (surface_desc.Width - 4));
			else
				// 其它列,左边一个像素就是当前像素减去4个字节等于D3DFMT_X8L8V8U8大小
				src_pixel_left = *(src_pixel_cur - 4);
			if(x == surface_desc.Width - 1)
				// 最后一列,右边一个像素等于第一列的像素
				src_pixel_right = *src_row_cur;
			else
				// 其它像素等于,右边一个像素
				src_pixel_right = *(src_pixel_cur + 4);
			// du是当前像素的左边第一个像素高度值(小端存储),减去右边一个像素的高度值
			BYTE du = BYTE(src_pixel_left - src_pixel_right);
			// dv是当前像素列的上一列减去下一列的高度值
			BYTE dv = BYTE(src_pixel_up   - src_pixel_below);
			// the luminance bump value
			// 目标当前行中的迭代像素值(高度),如果大于1那么亮度取63,否则取127
			BYTE u_lumi = (*src_pixel_cur > 1) ? 63 : 127;
			// 一个4字节的数值里面,格式为D3DFMT_X8L8V8U8,低数值存放在低地址,所以是先赋值du
			*dest_pixel++ = du;
			*dest_pixel++ = dv;
			*dest_pixel++ = u_lumi;
			*dest_pixel++ = 0;
			// move one pixel to the right
			// 当前源像素跳过4字节
			src_pixel_cur   += 4;
			// 源的下一行加上了4字节
			src_pixel_below += 4;	
			// 源的上一行也应该加4字节[by jiayuan 2015.8.2]
			src_pixel_up +=4;
			// 当前目标像素跳过4字节
			src_pixel_up     += 4;
		}
		// move to the next line
		// 一行结束,源像素行加宽度
		src_row_cur  += src_pitch;
		// 一行结束,目标像素加宽度
		dest_row_cur += dest_pitch;
	}
	pBump_map_texture->UnlockRect(0);
	pHeightMapTexture->UnlockRect(0);
	return S_OK;
}

3) 
凹凸纹理设置

凹凸纹理映射通常使用3层纹理:物体原始纹理、由原始纹理高度图生成的凹凸纹理、环境纹理,对应于多层纹理混合的0、1、2层。指定当前纹理层状态 为D3DTOP_BUMPENVMAP或D3DTOP_BUMPENVMAPLUMINANCE可设置当前纹理层为凹凸纹理,例如:

pd3dDevice->SetTexture(1, g_bump_map_texture);
// D3DTOP_BUMPENVMAP 没有亮度值,默认设置为1,和下一阶层纹理分量乘法输出是下一阶层的颜色
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP);

pd3dDevice->SetTexture(1, g_bump_map_texture);
// D3DTOP_BUMPENVMAPLUMINANCE 有亮度值需要和下阶层分量乘法输出颜色
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);

纹理状态D3DTOP_BUMPENVMAP和D3DTOP_BUMPENVMAPLUMINANCE表示两种不同的凹凸纹理映射方法。纹理状态D3DTOP_BUMPENVMAPLUMINANCE表示在凹凸纹理中包含凹凸纹理亮度值L,L将与下一纹理层的纹理颜色相乘作为最后输出的纹理颜色。纹理状态D3DTOP_BUMPENVMAP默认亮度值L为1,即不改变下一纹理层的纹理颜色。


4) 凹凸纹理的扰动矩阵和亮度系数亮度偏移的设置

Direct3D可设置凹凸纹理矩阵,对凹凸纹理中的每个纹理元素值(Dv、Du,即对于下一纹理层中的每个纹理坐标的扰动值)进行坐标变换:

Du‘ = Du * M00 + Dv * M10

Dv‘ = Dv * M01 + Dv * M11

凹凸纹理的每个纹理元素Dv、Du与一个2 x 2的凹凸矩阵相乘,其结果Dv、Du对下一纹理层中的每个纹理元素的纹理坐标产生该数值代表的坐标扰 动。2 x 2的凹凸矩阵值可由函数IDirect3DDevice9::SetTextureStageState()设置,将它的第一个参数设置为纹理层序号,第 二个参数设置为D3DTSS_BUMPENVMAT00或D3DTSS_BUMPENVMAT01或D3DTSS_BUMPENVMAT10或 D3DTSS_BUMPENVMAT11,分别表示凹凸矩阵的4个矩阵元素,第三个参数设置为该矩阵元素的值。纹理坐标扰动Dv、Du或Dv‘、Du‘的范围介于-1 ~ +1之间。

如果使用D3DTOP_BUMPENVMAPLUMINANCE计算凹凸纹理,Direct3D将使用下列方程计算凹凸纹理的亮度值L’(L’为0~255的整数,它将被用来与下一纹理层的颜色相乘,作为最后输出的纹理颜色值)。

L’ = L * S + O

其中,L为凹凸纹理元素中的亮度L值,比例系数S和偏移系数O可改变最终的亮度值。S和O可由函数 IDirect3DDevice9::SetTexureStageState()设置,将它的第一个参数设置为纹理层序号,第二个参数设置为 D3DTSS_BUMPENVLSCALE或D3DTSS_BUMPENVLOFFSET,分别表示凹凸纹理映射的比例系数S和偏移系数O,将第三个参数 设置为相应的系数值。

设置凹凸纹理状态的代码如下所示:

// set texture color blend method for stage 0 (base texture)

pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); 
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); 

pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

// set texture color blend method for stage 1 (bump map texture)

pd3dDevice->SetTexture(1, g_bump_map_texture);

pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);

pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);

pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT00, F2DW(0.8f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT01, F2DW(0.0f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT10, F2DW(0.0f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT11, F2DW(0.8f));


pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVLSCALE, F2DW(4.0f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVLOFFSET, F2DW(0.0f));


pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

// set texture color blend method for stage 2 (environment map texture)

5)凹凸纹理使用环境纹理的话,那么需要设置环境纹理的自动纹理坐标,和自动纹理坐标转换矩阵
D3DXMATRIX mat;

mat._11 = 0.5f; mat._12 = 0.0f; mat._13 = 0.0f; mat._14 = 0.0f; 
mat._21 = 0.0f; mat._22 = -0.5f; mat._23 = 0.0f; mat._24 = 0.0f; 
mat._31 = 0.0f; mat._32 = 0.0f; mat._33 = 1.0f; mat._34 = 0.0f; 
mat._41 = 0.5f; mat._42 = 0.5f; mat._43 = 0.0f; mat._44 = 1.0f; 

pd3dDevice->SetTransform(D3DTS_TEXTURE2, &mat);

pd3dDevice->SetTexture(2, g_env_map_texture);

pd3dDevice->SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

// 第0层第1层都是采用顶点uv或者用网格DrawSubset绘制,而第2阶层纹理采用了环境映射纹理用了自动生成坐标系
pd3dDevice->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP);

pd3dDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_ADD); 

pd3dDevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);


volume texture立体纹理:每个维度都是有像素值的

// check whether device support volume texture
if((pCaps->TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) == 0)
return false;

立体纹理(volume texture)是一组应用到二维图元(如一个三角形或一条直线)的三维纹理元素的集合,可以使用立体纹理实现一些特殊效果,如迷雾、爆炸等。当对一个图 元使用立体纹理时,它的每个顶点都需要一组三元纹理坐标。当绘制该图元时,它中间的每个像素都将用立体纹理中的一些纹理元素的颜色值进行填充,这与二维纹 理映射的情况相似。

立体纹理以薄片为单元组织起来,可以把它想象成将(宽 x 高)的二维表面转换成(宽 x 高 x 深)的三维立体,每个薄片是单独的一行,立体纹理可以有一系列级别,每一级都较上一级缩小一倍,类似二维的多幅渐进纹理,只不过这里是三维的

1) 声明顶点格式,使用立体纹理坐标

接着,我们需要在灵活顶点格式中指定每个顶点都需要三个纹理坐标,如下所示:

struct sCustomVertex
{
float x, y, z;
        // 一个立体纹理,是有x,y,z三个维度的,每个维度都是一个像素一层
float u, v, w;
};
#define D3DFVF_CUSTOM_VERTEX	(D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0))

2) 填充顶点缓存,为了显示立体纹理在不同深度上的表现,顶点纹理坐标的w值还是需要变化的

接着,为每个顶点指定数据,创建顶点缓冲区并填充它:

// create vertex buffer and fill data
sCustomVertex vertices[] = 	
{
{ -3.0f, -3.0f, 0.0f, 0.0f, 1.0f, 0.0f},
{ -3.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f},
{ 3.0f, -3.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{ 3.0f, 3.0f, 0.0f, 1.0f, 0.0f, 0.0f}
};
pd3dDevice->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOM_VERTEX, D3DPOOL_MANAGED, &g_vertex_buffer, NULL);
void* ptr;
g_vertex_buffer->Lock(0, sizeof(vertices), (void**)&ptr, 0);
memcpy(ptr, vertices, sizeof(vertices));
g_vertex_buffer->Unlock();

为了生成立体纹理,还必须改变纹理的w坐标:

//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
// change w coordinate of volume texture
	float angle = (float) fTime / 2.0f;
	sCustomVertex* ptr;
	g_vertex_buffer->Lock(0, 0, (void**)&ptr, 0);
        // 改变w值,也就是w在[0,1]的纹理坐标内切换,显示立体纹理中不同维度的平面纹理
	for(int i = 0; i < 4; i++)
ptr[i].w = sin(angle) * sin(angle);
	g_vertex_buffer->Unlock();
}

3) 创建和填充立体纹理

在示例程序中,立体纹理是由程序生成的,比较复杂。Direct3D提供了辅助函数D3DXCreateVolumeTextureFromFile()来从图片文件中生成立体纹理,该函数的声明如下:

Creates a volume texture from a file.

HRESULT D3DXCreateVolumeTextureFromFile(
LPDIRECT3DDEVICE9pDevice,
LPCTSTRpSrcFile,
LPDIRECT3DVOLUMETEXTURE9 *ppVolumeTexture
);

通过文件创建立体纹理时文件格式必须是立体纹理格式,对于普通格式的图片文件,可以通过DirectX提供的纹理工具”DirectX Texture Tool”转换成立体纹理格式的文件。

程序创建立体纹理

//创建立体纹理

 hr = pd3dDevice->CreateVolumeTexture( 16, 16, 16, 1, 0,

  D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,

  &g_pVolumeTexture, NULL );

 if( FAILED(hr) )

  return hr;

 //填充立体纹理

 D3DLOCKED_BOX LockedBox;

 hr = g_pVolumeTexture->LockBox( 0, &LockedBox, 0, 0 );

 if( FAILED(hr) )

  return hr;

 for( UINT w=0; w<16; w++ )

 {

  BYTE* pSliceStart = (BYTE*)LockedBox.pBits;

  for( UINT v=0; v<16; v++ )

  {

   for( UINT u=0; u<16; u++ )

   {

    // du,dv,dw从[-1,1]的浮点数

    FLOAT du = (u-7.5f)/7.5f;

    FLOAT dv = (v-7.5f)/7.5f;

    FLOAT dw = (w-7.5f)/7.5f;

    FLOAT fScale = sqrtf( du*du + dv*dv + dw*dw ) / sqrtf(1.0f);

    if( fScale > 1.0f )

    {

       fScale = 0.0f;

    }

    // 取反是因为越近,颜色越明亮,越远越暗淡

    else

    {

       fScale = 1.0f – fScale;

    }

    // rgb颜色

    DWORD r = (DWORD)((w<<4)*fScale);

    DWORD g = (DWORD)((v<<4)*fScale);

    DWORD b = (DWORD)((u<<4)*fScale);

    // 填充好二维的

    ((DWORD*)LockedBox.pBits)[u] = 0xff000000 + (r<<16) + (g<<8) + (b);

   }

   // 下一行

   LockedBox.pBits = (BYTE*)LockedBox.pBits + LockedBox.RowPitch;

  }

  // 深度上面也是要逐像素计算颜色的,也是和2维一样分作了16像素

  // 那么最终得到的立体纹理是一个 16X16X16的纹理内存,故可以模拟迷糊、爆炸效果.

  LockedBox.pBits = pSliceStart + LockedBox.SlicePitch;

 }

 g_pVolumeTexture->UnlockBox( 0 );

接着,为每个顶点指定数据,创建顶点缓冲区并填充它:

// create vertex buffer and fill data
sCustomVertex vertices[] = 	
{
{ -3.0f, -3.0f, 0.0f, 0.0f, 1.0f, 0.0f},
{ -3.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f},
{ 3.0f, -3.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{ 3.0f, 3.0f, 0.0f, 1.0f, 0.0f, 0.0f}
};
4)渲染立体纹理

最后设置要渲染的纹理层和立体纹理,纹理阶段混合方法,顶点数据,并渲染它:

// set textue and stream source and vertex format
pd3dDevice->SetTexture(0, g_volume_texture);
pd3dDevice->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));
pd3dDevice->SetFVF(D3DFVF_CUSTOM_VERTEX);
// set texture color blend method
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);

pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Clear the render target and the zbuffer 
V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0) );
// Render the scene
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{
pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
RenderText();
	V(g_button_dlg.OnRender(fElapsedTime));
V( pd3dDevice->EndScene() );
}

立体纹理和普通二维纹理的主要区别是:顶点纹理坐标是三维的,纹理对象指针的类型不同,立体纹理的创建也不同,而立体纹理在使用上和普通二维纹理基本相同。

纹理压缩技术应用

要渲染看起来真实的场景,最好是使用高分辨率而且颜色丰富的纹理,但这样的纹理可能会耗费大量的内存,例如,一张每像素16位颜色的256x 256纹理将使用128KB的内存。如果在该纹理中使用多级渐进纹理,还需要额外的43KB内存。一个使用50张这种纹理的场景将需要8MB的内存,如果需要更强的真实性,可以使用每像素32位颜色的512x 512纹理,但那就需要8倍的内存。为了减少纹理消耗的系统带宽和内存空间,Direct3D支持纹理压缩和实时解压,即DXT纹理压缩。压缩后的纹理被存储在Direct3D纹理指针中,当Direct3D渲染物体时,Direct3D引擎自动对纹理进行解压。应用DXT压缩纹理不仅可以节省内存空间,而且能有效地降低纹理传输带宽,提高图形系统的整体性能。

随着DirectX对纹理压缩格式的推广,目前大部分显卡都支持DXT压缩纹理,而且DXT压缩纹理在图形质量和运行速度之间取得了很好的平衡。

DXT纹理压缩格式

DXT是一种DirectDraw表面,它以压缩形式存储图形数据,该表面可以节省大量的系统带宽和内存。即使不直接使用DXT表面渲染,也可以通 过DXT格式创建纹理的方法节省磁盘空间。Direct3D提供了D3DFMT_DXT1~D3DFMT_DXT5共5种压缩纹理格式。其中,D3DFMT_DXT1支持15位RGB和1位alpha图形格式,D3DFMT_DXT2、D3DFMT_DXT3支持12位RGB和4位alpha,D3DFMT_DXT4、D3DFMT_DXT5则采取了线性插值方式生成alpha。

有3种DXTC的格式可供使用,分别是DXT1,DXT3和DXT5。
DXT1 压缩比例:1:8 压缩比最高,它只有1Bit Alpha,Alpha通道信息几乎完全丧失。一般将不带Alpha通道的图片压缩成这种格式。如Worldwind 用的卫星图片。
DXT3 压缩比例:1:4 使用了4Bit Alpha,可以有16个Alpha值,可很好地用于alpha通道锐利、对比强烈的半透和镂空材质。
DXT5 压缩比例:1:4 使用了线形插值的4Bit Alpha,特别适合Alpha通道柔和的材质,比如高光掩码材质。
大部分3D游戏引擎都可以使用DDS格式的图片用作贴图,也可以制作法线贴图
DXT纹理压缩的应用:
1)检查是否支持纹理压缩
// check whether device support DXT compressed texture
if(FAILED(pD3D->CheckDeviceFormat(pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT1)))
return false;
如果当前设备支持DXT压缩纹理,就可以直接创建DXT压缩纹理,并可以通过IDirect3DDevice9::SetTexture()函数设置
DXT压缩纹理,将其映射到物体表面。如果当前设备不支持DXT压缩纹理,仍然可以DXT压缩格式存储纹理,但在使用该纹理渲染图形前,必须将DXT压缩
格式的纹理转换为当前设备支持的格式。

2) 创建dds格式文件,非dds格式文件也是可以使用压缩纹理的,但是dds格式可以压缩贴图大小且加载更快

ps查看和导出dds插件:https://developer.nvidia.com/nvidia-texture-tools-adobe-photoshop

    
3dsmax现在的版本都应该支持dds贴图作为才材质的了, 不过通过http://www.nvidia.cn/object/3dsmax_dds_plugins.html下载的原码可以窥探一下nvidia写的导入 dds的 插件代码。
dds文件格式编辑 DirectX Texture Tool 可以编辑dds文件使用的压缩格式DXT1,DXT3,DXT5等,可以编辑文件mipMap/CubeMap/VolumeMap纹理特性。可以查看alpha通道信息等,具体使用见 https://msdn.microsoft.com/en-us/library/bb219744.aspx

3)创建压缩纹理
一般使用DXT1,DXT3和DXT5类型的压缩纹理

在使用函数IDirect3DDevice9::CreateTexture()创建纹理时,将参数Format设置为压缩纹理格式D3DFMT_DXT1 ~ D3DFMT_DXT5中的任意值,可以创建指定压缩格式的纹理,也可以通过函数D3DXCreateTextureFromFileEx()从磁盘文件中生成DXT压缩纹理:

D3DXCreateTextureFromFileExW(pd3dDevice, L"texture.jpg", 0, 0, 5, 0, D3DFMT_DXT1, D3DPOOL_MANAGED,D3DX_DEFAULT, D3DX_DEFAULT, 0xFF000000, NULL, NULL, &g_texture);

4)使用压缩纹理

在创建好压缩纹理之后,就可以和前面一样设置纹理及各项纹理渲染状态,最后进行渲染。如果当前设备支持DXT压缩纹理,那么在使用DXT纹理渲染图形时,Direct3D引擎会自动进行纹理压缩,如果当前设备不支持DXT压缩纹理,这时使用DXT压缩纹理和使用普通的非压缩纹理完全相同。当使用大量纹理贴图渲染复杂场景时,通过使用DXT压缩纹理可以有效提高程序运行性能。


13.纹理管理

如果显卡中只有64MB的视频内存,就应该考虑将哪些纹理加载和保留在内存中。可以编写一个算法让计算机来自动考虑这些问题,那么这个算法的任务是什么呢?它需要跟踪可用的纹理内存总数,并了解哪些纹理是经常用到的,而哪些纹理是很少用到的。至少,这个纹理管理算法必须能决定哪些现有的纹理资源可以通过一张纹理图来重新加载,以及应该销毁哪些表面,然后由新的纹理资源取而代之。

Direct3D有一个自动的纹理管理系统,当通过CreateTexture()创建一个纹理对象时,将Pool参数指定为D3DPOOL_MANAGED,就是向Direct3D请求该系统的支持。纹理管理器通过一个时间戳来跟踪纹理的使用情况,这个时间戳用于记录每个纹理 对象最后被使用的时间。纹理管理器通过"最近最少使用"算法来决定哪些纹理应从视频内存中移除,若有两个纹理对象同时满足移除条件,则根据纹理属性来做进一步的判断。如果它们具有相同的优先级,则移除最近最少使用的纹理,如果它们具有相同的时间戳,则移除低优先级的纹理。

通过为纹理表面调用IDirect3DResource9::SetPriority()函数,可以给托管的纹理赋予一个优先级,该函数声明如下:

Assigns the resource-management priority for this resource.

DWORD SetPriority(
DWORDPriorityNew
);

纹理优化方式

0)使用dds格式文件,使用D3DFMT_DXTn来创建内存压缩纹理。
1)将多个小图片,用TexturePacker合并为一张大图和xml配置文件,读取小图时候根据xml截取矩形区域即可。对称的纹理只需要一张即可,然后通过旋转变换。符合九宫格的图片也是只需要一张即可进行放大。太大的图片也可以切割然后进行拼接得到需要的大图片。
2)使用D3DPOOL_DEFAULT常驻显存中,避免每次都调度提交,将静态不经常变更的纹理放置在显存中(例如光照贴图,地图,建筑等资源),渲染效率会更高,丢失的时候IDirect3DDevice9::Reset后要重新创建恢复起来要不断的传输到显存中有点慢有时候如果是比较大量的贴图建议使用D3DPOOL_MANAGED,并且显存中的纹理不能锁定更新需要IDirect3DDevice9::UpdateSurface, IDirect3DDevice9::UpdateTexture;和纹理不同显存中的swapchain back buffers, render targets, vertex buffers和indexbuffers都是可以锁定更新的。
常驻内存,有频繁更新的数据,例如粒子数据,骨骼蒙皮动画等也是可以声明为D3DPOOL_DEFAULT的,同时用D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY 使用方式标记,这意味着此资源会被创建在AGP Memory中。锁定的时候用D3DLOCK_NOOVERWRITE或D3DLOCK_DISCARD可以瞒天过海避免同步,且可以修改一部分渲染一部分提高CPU和GPU的并行效率。
使用D3DPOOL_MANAGED
交给D3D托管,渲染效率一般(系统RAM/AGP内存/显存中都有)

D3DPOOL_MANAGED的资源可以自由的锁定。

但是只有系统内存中的副本资源数据可以直接的修改,当需要渲染的时候D3D会自动的拷贝到AGP内存和显存中。

适用情况:由于D3D自己的资源管理方案很高效、使用简单,游戏中大部分资源都可以使用此标识创建

例子:游戏中使用的大部分纹理贴图,静态模型


D3DPOOL_SYSTEMMEM:

直接放置在系统中,渲染效率较差需要用UpdateSurface或UpdateTexture拷贝到D3DPOOL_DEFAULT创建的资源,设备丢失时候不需要重新创建资源,锁定很方便CPU处理较快。这些资源可以被锁定(读写),可以通过IDirect3DDevice9::UpdateSurfaceor IDirect3DDevice9::UpdateTexture函数,将该类型的资源数据,设置为用D3DPOOL_DEFAULT创建的资源的数据来源。

使用:对于需要高频CPU读写处理的数据,推荐使用这种方式。

D3DPOOL_SCRATCH:

直接放置在系统中,不能直接的交给D3D渲染,设备丢失时候不需要创建资源,不被设备像素格式限制可以创建锁定拷贝。

这些资源放置在系统RAM中,当设备丢失时不需要重新创建数据。这些数据不被设备大小和像素格式的限制。正因为如此,

这些资源不能被Direct3D访问,不能够设置为纹理或者设置为渲染目标。然而,这些资源之间总是可以被创建,锁定和拷贝。

Directx 9引入这种内存池的原因是由于它从API中删除了CreateImageSurface()的函数。通常,D3DPOOL_SCRATCH是连同CreateOffscreenPlainSurface()一起使用的,后者将返回一个与之前通过CreateImageSurface()来创建的、具有相同特征的表面。因此,它主要用于创建图像表面

4)使用内存池管理纹理,避免每次都从磁盘IO加载纹理,当不需要的时候及时释放,过关卡时候释放掉管理的一些场景静态纹理内存,建筑纹理,特效等等。对于纹理这时可以调用IDirect3D3::EvictManagedTextures方法逐出所有的被管理纹理。在调用这个方法时,Direct3D会将所有的本地与非本地显存纹理销毁掉,只留下原始系统内存拷贝。

5)异步读取纹理,提高IO性能,但是一定要采用异步线程机制,唤醒回调的机制,避免不断的循环浪费CPU时间。

6)暂时没有想到其它减少cpu draw call的技巧,减少内存和飙升大小的技巧,减少IO读取和提高IO读取性能,减少GPU调度和渲染纹理的技巧。