以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  高级纹理映射技术(4)  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=70312)


--  作者:卷积内核
--  发布时间:12/10/2008 8:24:00 AM

--  高级纹理映射技术(4)
凹凸纹理映射

关于凹凸映射的原理请参阅凹凸映射(Bump Map)实现原理

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

按此在新窗口浏览图片

检查硬件设备

在使用凹凸纹理映射之前,应查询当前的Direct3D设备是否支持D3DTEXOPCAPS_BUMPENVMAP或D3DTEXOPCAPS_BUMPENVMAPLUMINANCE多层纹理混合,以及当前设备是否支持3层纹理映射。

BOOL SupportsBumpMapping(){    D3DCAPS9 d3dCaps;    d3dDevice->GetDeviceCaps( &d3dCaps );
    // Does this device support the two bump mapping blend operations?    if ( 0 == d3dCaps.TextureOpCaps & ( D3DTEXOPCAPS_BUMPENVMAP | D3DTEXOPCAPS_BUMPENVMAPLUMINANCE ))        return FALSE;
    // Does this device support up to three blending stages?    if( d3dCaps.MaxTextureBlendStages < 3 )        return FALSE;
    return TRUE;}
如果当前硬件不支持上面的任何一项,程序框架会自动转而使用参考设备。

凹凸纹理生成

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  )

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

//--------------------------------------------------------------------------------------// Create bump texture from height map texture.//--------------------------------------------------------------------------------------HRESULT CreateBumpTexture(IDirect3DDevice9* device){ HRESULT hr;
 D3DSURFACE_DESC surface_desc; g_height_map_texture->GetLevelDesc(0, &surface_desc);
 V_RETURN(device->CreateTexture(surface_desc.Width, surface_desc.Height, 1, 0, D3DFMT_X8L8V8U8, D3DPOOL_MANAGED,           &g_bump_map_texture, NULL));
 D3DLOCKED_RECT locked_rect; g_height_map_texture->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);
 g_bump_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;
  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)    src_pixel_left = *(src_row_cur + (surface_desc.Width - 4));   else    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);
   BYTE du = BYTE(src_pixel_left - src_pixel_right);   BYTE dv = BYTE(src_pixel_up   - src_pixel_below);
   // the luminance bump value   BYTE u_lumi = (*src_pixel_cur > 1) ? 63 : 127;
   *dest_pixel++ = du;   *dest_pixel++ = dv;   *dest_pixel++ = u_lumi;   *dest_pixel++ = 0;
   // move one pixel to the right   src_pixel_cur   += 4;   src_pixel_up     += 4;   src_pixel_below += 4;     }
  // move to the next line  src_row_cur  += src_pitch;  dest_row_cur += dest_pitch; }
 g_bump_map_texture->UnlockRect(0); g_height_map_texture->UnlockRect(0);
 return S_OK;}

凹凸纹理设置

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

pd3dDevice->SetTexture(1, g_bump_map_texture);pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP,     D3DTOP_BUMPENVMAP);

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

这里分别使用了3层纹理贴图,如下所示:

按此在新窗口浏览图片
原始纹理

按此在新窗口浏览图片
原始纹理高度图

按此在新窗口浏览图片
环境纹理贴图

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)

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

按此在新窗口浏览图片
原始纹理图

按此在新窗口浏览图片
经过凹凸纹理映射后


--  作者:卷积内核
--  发布时间:12/10/2008 8:25:00 AM

--  
主程序:


#include "dxstdafx.h"
#include "resource.h"

#pragma warning(disable : 4127 4995 4996)

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

#define IDC_TOGGLE_FULLSCREEN        1
#define IDC_TOGGLE_REF                2
#define IDC_CHANGE_DEVICE            3

const D3DXCOLOR FONT_COLOR(1.0f, 0.5f, 0.25f, 1.0f);

ID3DXFont*                    g_font;
ID3DXSprite*                g_text_sprite;
bool                        g_show_help;

CDXUTDialogResourceManager    g_dlg_resource_manager;
CD3DSettingsDlg                g_settings_dlg;
CDXUTDialog                    g_button_dlg;

ID3DXMesh*                    g_mesh;
D3DMATERIAL9*                g_mesh_materials;
IDirect3DTexture9**            g_mesh_textures;
DWORD                        g_num_materials;

IDirect3DTexture9*            g_env_map_texture;
IDirect3DTexture9*            g_height_map_texture;
IDirect3DTexture9*            g_bump_map_texture;

inline DWORD F2DW(float f) { return *((DWORD*) &f); }

//--------------------------------------------------------------------------------------
// Rejects any devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
                                  D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
    // Typically want to skip backbuffer formats that don't support alpha blending

    IDirect3D9* pD3D = DXUTGetD3DObject();

    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat,
                    D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

    // check whether device support bump textue mapping
    if((pCaps->TextureOpCaps & (D3DTEXOPCAPS_BUMPENVMAP | D3DTEXOPCAPS_BUMPENVMAPLUMINANCE)) == 0)
        return false;

    // check whether device support 3 level texture stage blend
    if(pCaps->MaxTextureBlendStages < 3)
        return false;

    return true;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed.
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext )
{
    // If video card does not support hardware vertex processing, then uses sofaware vertex processing.
    if((pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

    static bool is_first_time = true;

    if(is_first_time)
    {
        is_first_time = false;

        // if using reference device, then pop a warning message box.
        if(pDeviceSettings->DeviceType == D3DDEVTYPE_REF)
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}

//--------------------------------------------------------------------------------------
// Create bump texture from height map texture.
//--------------------------------------------------------------------------------------
HRESULT CreateBumpTexture(IDirect3DDevice9* device)
{
    HRESULT hr;

    D3DSURFACE_DESC surface_desc;
    g_height_map_texture->GetLevelDesc(0, &surface_desc);

    V_RETURN(device->CreateTexture(surface_desc.Width, surface_desc.Height, 1, 0, D3DFMT_X8L8V8U8, D3DPOOL_MANAGED,
                                   &g_bump_map_texture, NULL));


    D3DLOCKED_RECT locked_rect;
    g_height_map_texture->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);

    g_bump_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;

        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)
                src_pixel_left = *(src_row_cur + (surface_desc.Width - 4));
            else
                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);

            BYTE du = BYTE(src_pixel_left - src_pixel_right);
            BYTE dv = BYTE(src_pixel_up   - src_pixel_below);

            // the luminance bump value
            BYTE u_lumi = (*src_pixel_cur > 1) ? 63 : 127;

            *dest_pixel++ = du;
            *dest_pixel++ = dv;
            *dest_pixel++ = u_lumi;
            *dest_pixel++ = 0;

            // move one pixel to the right
            src_pixel_cur   += 4;
            src_pixel_up    += 4;
            src_pixel_below += 4;            
        }

        // move to the next line
        src_row_cur  += src_pitch;
        dest_row_cur += dest_pitch;
    }

    g_bump_map_texture->UnlockRect(0);
    g_height_map_texture->UnlockRect(0);

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Remove path from fullname, and convert filename from multibyte to wchar.
//--------------------------------------------------------------------------------------
void RemovePathFromFileName(LPSTR fullname, LPWSTR wfilename)
{
    WCHAR wbuf[MAX_PATH]  = {0};
    MultiByteToWideChar(CP_ACP, 0, fullname, -1, wbuf, MAX_PATH);

    LPWSTR w_last_back_slash = wcsrchr(wbuf, '\\');

    if(w_last_back_slash)
        lstrcpy(wfilename, ++w_last_back_slash);
    else
        lstrcpy(wfilename, wbuf);
}

//--------------------------------------------------------------------------------------
// Create any D3DPOOL_MANAGED resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice,
                                 const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                 void* pUserContext )
{
    HRESULT    hr;

    V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice));
    V_RETURN(g_settings_dlg.OnCreateDevice(pd3dDevice));

    D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
                   DEFAULT_PITCH | FF_DONTCARE, L"Arial", &g_font);

    V_RETURN(D3DXCreateTextureFromFile(pd3dDevice, L"HeightMap.bmp", &g_height_map_texture));
    V_RETURN(D3DXCreateTextureFromFile(pd3dDevice, L"EnvMap.bmp",     &g_env_map_texture));
    V_RETURN(CreateBumpTexture(pd3dDevice));
    
    ID3DXBuffer* material_buffer;

    V_RETURN(D3DXLoadMeshFromXW(L"SphereEarth.x", D3DXMESH_MANAGED, pd3dDevice, NULL, &material_buffer, NULL,
                                &g_num_materials, &g_mesh));

    D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();
    g_mesh_materials = new D3DMATERIAL9[g_num_materials];
    g_mesh_textures  = new IDirect3DTexture9*[g_num_materials];

    for(DWORD i = 0; i < g_num_materials; i++)
    {
        g_mesh_materials[i] = xmaterials[i].MatD3D;

        // .x file do not save ambient data, so set it here.
        g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse;         

        WCHAR wfilename[MAX_PATH];        
        RemovePathFromFileName(xmaterials[i].pTextureFilename, wfilename);

        g_mesh_textures[i] = NULL;

        if(xmaterials[i].pTextureFilename != NULL && lstrlen(wfilename) > 0)        
            V_RETURN(D3DXCreateTextureFromFile(pd3dDevice, wfilename, &g_mesh_textures[i]));
    }

    material_buffer->Release();

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_DEFAULT resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice,
                                const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                void* pUserContext )
{
    HRESULT hr;

    V_RETURN(g_dlg_resource_manager.OnResetDevice());
    V_RETURN(g_settings_dlg.OnResetDevice());
    V_RETURN(g_font->OnResetDevice());
    V_RETURN(D3DXCreateSprite(pd3dDevice, &g_text_sprite));

    // set dialog position and size

    g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0);
    g_button_dlg.SetSize(170, 170);

    // setup view matrix

    D3DXMATRIX mat_view;
    D3DXVECTOR3 eye(0.0f, -3.0f, 0.0f);
    D3DXVECTOR3  at(0.0f, 0.0f,  0.0f);
    D3DXVECTOR3  up(0.0f, 0.0f,  1.0f);

    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    pd3dDevice->SetTransform(D3DTS_VIEW, &mat_view);

    // set projection matrix
    D3DXMATRIX mat_proj;
    float aspect = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, aspect, 1.0f, 1000.0f);
    pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_proj);    

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

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

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Release resources created in the OnResetDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnLostDevice();
    g_settings_dlg.OnLostDevice();
    g_font->OnLostDevice();

    release_com(g_text_sprite);
}


//--------------------------------------------------------------------------------------
// Release resources created in the OnCreateDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnDestroyDevice();
    g_settings_dlg.OnDestroyDevice();    

    delete[] g_mesh_materials;
    g_mesh_materials = NULL;

    if(g_mesh_textures)
    {
        for(DWORD i = 0; i < g_num_materials; i++)
            release_com(g_mesh_textures[i]);

        delete[] g_mesh_textures;
        g_mesh_textures = NULL;
    }

    release_com(g_font);
    release_com(g_mesh);
    release_com(g_env_map_texture);
    release_com(g_height_map_texture);
    release_com(g_bump_map_texture);
}

//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    D3DXMATRIX mat_world;
    D3DXMatrixRotationZ(&mat_world, timeGetTime()/1000.0f);
    pd3dDevice->SetTransform(D3DTS_WORLD, &mat_world);
}

//--------------------------------------------------------------------------------------
// Render the helper information
//--------------------------------------------------------------------------------------
void RenderText()
{
    CDXUTTextHelper text_helper(g_font, g_text_sprite, 20);
    
    text_helper.Begin();

    // show frame and device states
    text_helper.SetInsertionPos(5, 5);
    text_helper.SetForegroundColor(FONT_COLOR);
    text_helper.DrawTextLine( DXUTGetFrameStats(true) );
    text_helper.DrawTextLine( DXUTGetDeviceStats() );

    // show helper information
    
    const D3DSURFACE_DESC* surface_desc = DXUTGetBackBufferSurfaceDesc();

    if(g_show_help)
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 18 * 5);
        text_helper.SetForegroundColor(FONT_COLOR);
        text_helper.DrawTextLine(L"Controls (F1 to hide):");
        
        text_helper.SetInsertionPos(40, surface_desc->Height - 18 * 4);
        text_helper.DrawTextLine(L"Quit: ESC");
    }
    else
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 4);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) );
        text_helper.DrawTextLine(L"Press F1 for help");
    }

    text_helper.End();
}

//--------------------------------------------------------------------------------------
// Render the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.OnRender(fElapsedTime);
        return;
    }

    // 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() ) )
    {
        for(DWORD i = 0; i < g_num_materials; i++)
        {
            pd3dDevice->SetMaterial(&g_mesh_materials[i]);
            pd3dDevice->SetTexture(0, g_mesh_textures[i]);
            g_mesh->DrawSubset(i);
        }

        RenderText();

        V(g_button_dlg.OnRender(fElapsedTime));

        V( pd3dDevice->EndScene() );
    }
}


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
                          bool* pbNoFurtherProcessing, void* pUserContext )
{
    *pbNoFurtherProcessing = g_dlg_resource_manager.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
        return 0;
    }

    *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    return 0;
}


//--------------------------------------------------------------------------------------
// Handle keybaord event
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboardProc(UINT charater, bool is_key_down, bool is_alt_down, void* user_context)
{
    if(is_key_down)
    {
        switch(charater)
        {
        case VK_F1:
            g_show_help = !g_show_help;
            break;
        }
    }
}

//--------------------------------------------------------------------------------------
// Handle events for controls
//--------------------------------------------------------------------------------------
void CALLBACK OnGUIEvent(UINT event, int control_id, CDXUTControl* control, void* user_context)
{
    switch(control_id)
    {
    case IDC_TOGGLE_FULLSCREEN:
        DXUTToggleFullScreen();
        break;

    case IDC_TOGGLE_REF:
        DXUTToggleREF();
        break;

    case IDC_CHANGE_DEVICE:
        g_settings_dlg.SetActive(true);
        break;
    }
}

//--------------------------------------------------------------------------------------
// Initialize dialogs
//--------------------------------------------------------------------------------------
void InitDialogs()
{
    g_settings_dlg.Init(&g_dlg_resource_manager);
    g_button_dlg.Init(&g_dlg_resource_manager);

    g_button_dlg.SetCallback(OnGUIEvent);

    int x = 35, y = 10, width = 125, height = 22;

    g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y,         width, height);
    g_button_dlg.AddButton(IDC_TOGGLE_REF,          L"Toggle REF (F3)",     x, y += 24, width, height);
    g_button_dlg.AddButton(IDC_CHANGE_DEVICE,      L"Change device (F2)", x, y += 24, width, height, VK_F2);    
}

//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // Set the callback functions
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard(OnKeyboardProc);
   
    // TODO: Perform any application-level initialization here
    InitDialogs();

    // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"Bump Texture Mapping" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, IsDeviceAcceptable, ModifyDeviceSettings );

    // Start the render loop
    DXUTMainLoop();

    // TODO: Perform any application-level cleanup here

    return DXUTGetExitCode();
}

[B][URL=http://www.cppblog.com/Files/changingnow/BumpTextureMapping.rar]下载示例工程[/URL][/B]


--  作者:卷积内核
--  发布时间:12/10/2008 8:26:00 AM

--  
立体纹理

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

立体纹理以薄片为单元组织起来,可以把它想象成将(宽 x 高)的二维表面转换成(宽 x 高 x 深)的三维立体,每个薄片是单独的一行,立体纹理可以有一系列级别,每一级都较上一级缩小一倍,如下图所示:

按此在新窗口浏览图片

要创建立体纹理,首先必须检查显卡是否支持立体纹理:

// check whether device support volume textureif((pCaps->TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) == 0) return false;
接着,我们需要在灵活顶点格式中指定每个顶点都需要三个纹理坐标,如下所示:

struct sCustomVertex{ float x, y, z; float u, v, w;};
#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0))
D3DFVF_TEXCOORDSIZEN
Constructs bit patterns that are used to identify texture coordinate formats within a FVF description. The results of these macros can be combined within a FVF description by using the OR operator.

#define D3DFVF_TEXCOORDSIZEN(CoordIndex) #define D3DFVF_TEXCOORDSIZE1(CoordIndex) (D3DFVF_TEXTUREFORMAT1 << (CoordIndex*2 + 16)) #define D3DFVF_TEXCOORDSIZE2(CoordIndex) (D3DFVF_TEXTUREFORMAT2) #define D3DFVF_TEXCOORDSIZE3(CoordIndex) (D3DFVF_TEXTUREFORMAT3 << (CoordIndex*2 + 16)) #define D3DFVF_TEXCOORDSIZE4(CoordIndex) (D3DFVF_TEXTUREFORMAT4 << (CoordIndex*2 + 16))
Parameters
CoordIndex
Value that identifies the texture coordinate set at which the texture coordinate size (1-, 2-, 3-, or 4Dimensional) applies.
Remarks
The D3DFVF_TEXCOORDSIZEN macros use the following constants.

#define D3DFVF_TEXTUREFORMAT1 3 // one floating point value#define D3DFVF_TEXTUREFORMAT2 0 // two floating point values#define D3DFVF_TEXTUREFORMAT3 1 // three floating point values#define D3DFVF_TEXTUREFORMAT4 2 // four floating point values
The following FVF description identifies a vertex format that has a position; a normal; diffuse and specular colors; and two sets of texture coordinates. The first set of texture coordinates includes a single element, and the second set includes two elements:

DWORD dwFVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE |              D3DFVF_SPECULAR | D3DFVF_TEX2 |              D3DFVF_TEXCOORDSIZE1(0) |  // Uses 1D texture coordinates for texture coordinate set 1 (index 0).              D3DFVF_TEXCOORDSIZE2(1);   // And 2D texture coordinates for texture coordinate set 2 (index 1).

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

// 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();
可通过IDirect3DDevice9::CreateVolumeTexture()来创建立体纹理,该函数声明如下:

Creates a volume texture resource.

HRESULT CreateVolumeTexture(  UINT Width,  UINT Height,  UINT Depth,  UINT Levels,  DWORD Usage,  D3DFORMAT Format,  D3DPOOL Pool,  IDirect3DVolumeTexture9** ppVolumeTexture,  HANDLE* pSharedHandle);
Parameters
Width
[in] Width of the top-level of the volume texture, in pixels. This value must be a power of two if the D3DPTEXTURECAPS_VOLUMEMAP_POW2 member of D3DCAPS9 is set. The pixel dimensions of subsequent levels will be the truncated value of half of the previous level's pixel dimension (independently). Each dimension clamps at a size of 1 pixel. Thus, if the division by two results in 0 (zero), 1 will be taken instead. The maximum dimension that a driver supports (for width, height, and depth) can be found in MaxVolumeExtent in D3DCAPS9.
Height
[in] Height of the top-level of the volume texture, in pixels. This value must be a power of two if the D3DPTEXTURECAPS_VOLUMEMAP_POW2 member of D3DCAPS9 is set. The pixel dimensions of subsequent levels will be the truncated value of half of the previous level's pixel dimension (independently). Each dimension clamps at a size of 1 pixel. Thus, if the division by 2 results in 0 (zero), 1 will be taken instead. The maximum dimension that a driver supports (for width, height, and depth) can be found in MaxVolumeExtent in D3DCAPS9.
Depth
[in] Depth of the top-level of the volume texture, in pixels. This value must be a power of two if the D3DPTEXTURECAPS_VOLUMEMAP_POW2 member of D3DCAPS9 is set. The pixel dimensions of subsequent levels will be the truncated value of half of the previous level's pixel dimension (independently). Each dimension clamps at a size of 1 pixel. Thus, if the division by 2 results in 0 (zero), 1 will be taken instead. The maximum dimension that a driver supports (for width, height, and depth) can be found in MaxVolumeExtent in D3DCAPS9.
Levels
[in] Number of levels in the texture. If this is zero, Direct3D will generate all texture sublevels down to 1x1 pixels for hardware that supports mipmapped volume textures. Call IDirect3DBaseTexture9::GetLevelCount to see the number of levels generated.
Usage
[in] Usage can be 0, which indicates no usage value. If usage is desired, use D3DUSAGE_DYNAMIC or D3DUSAGE_SOFTWAREPROCESSING. For more information, see D3DUSAGE.
Format
[in] Member of the D3DFORMAT enumerated type, describing the format of all levels in the volume texture.
Pool
[in] Member of the D3DPOOL enumerated type, describing the memory class into which the volume texture should be placed.
ppVolumeTexture
[out, retval] Address of a pointer to an IDirect3DVolumeTexture9 interface, representing the created volume texture resource.
pSharedHandle
[in] Reserved. Set this parameter to NULL.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DERR_OUTOFVIDEOMEMORY, E_OUTOFMEMORY.

示例代码如下:

HRESULT hr;
UINT width = 16, height = 16, depth = 16;

V_RETURN(device->CreateVolumeTexture(width, height, depth, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
&g_volume_texture, NULL));

接着调用IDirect3DVolumeTexture9::LockBox()来锁定立体纹理顶点缓冲区,该函数的声明如下:

Locks a box on a volume texture resource.

HRESULT LockBox(  UINT Level,  D3DLOCKED_BOX * pLockedVolume,  CONST D3DBOX * pBox,  DWORD Flags);
Parameters
Level
[in] Specifies the level of the volume texture resource to lock.
pLockedVolume
[out] Pointer to a D3DLOCKED_BOX structure, describing the locked region.
pBox
[in] Pointer to the volume to lock. This parameter is specified by a pointer to a D3DBOX structure. Specifying NULL for this parameter locks the entire volume level.
Flags
[in] Combination of zero or more locking flags that describe the type of lock to perform. For this method, the valid flags are:
D3DLOCK_DISCARD
D3DLOCK_NO_DIRTY_UPDATE
D3DLOCK_NOSYSLOCK
D3DLOCK_READONLY
For a description of the flags, see D3DLOCK.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.

Remarks
For performance reasons, dirty regions are only recorded for level zero of a texture. Dirty regions are automatically recorded when IDirect3DVolumeTexture9::LockBox is called without D3DLOCK_NO_DIRTY_UPDATE or D3DLOCK_READONLY. For more information, see IDirect3DDevice9::UpdateTexture.

D3DLOCKED_BOX
Describes a locked box (volume).

typedef struct D3DLOCKED_BOX {    int RowPitch;    int SlicePitch;    void * pBits;} D3DLOCKED_BOX, *LPD3DLOCKED_BOX;
Members
RowPitch
Byte offset from the left edge of one row to the left edge of the next row.
SlicePitch
Byte offset from the top-left of one slice to the top-left of the next deepest slice.
pBits
Pointer to the beginning of the volume box. If a D3DBOX was provided to the LockBox call, pBits will be appropriately offset from the start of the volume.
Remarks
Volumes can be visualized as being organized into slices of width x height 2D surfaces stacked up to make a width x height x depth volume. For more information, see Volume Texture Resources (Direct3D 9).

D3DBOX
Defines a volume.

typedef struct D3DBOX {    UINT Left;    UINT Top;    UINT Right;    UINT Bottom;    UINT Front;    UINT Back;} D3DBOX, *LPD3DBOX;
Members
Left
Position of the left side of the box on the x-axis.
Top
Position of the top of the box on the y-axis.
Right
Position of the right side of the box on the x-axis.
Bottom
Position of the bottom of the box on the y-axis.
Front
Position of the front of the box on the z-axis.
Back
Position of the back of the box on the z-axis.
Remarks
D3DBOX includes the left, top, and front edges; however, the right, bottom, and back edges are not included. For example, a box that is 100 units wide and begins at 0 (thus, including the points up to and including 99) would be expressed with a value of 0 for the Left member and a value of 100 for the Right member. Note that a value of 99 is not used for the Right member.

The restrictions on side ordering observed for D3DBOX are left to right, top to bottom, and front to back.

示例代码如下:

D3DLOCKED_BOX locked_box;g_volume_texture->LockBox(0, &locked_box, NULL, 0);
for(UINT w = 0; w < depth; w++){ BYTE* slice_start = (BYTE*) locked_box.pBits;
 for(UINT v = 0; v < height; v++) {  for(UINT u = 0; u < width; u++)  {   // create texel color and fill it
   float du = (u - 7.5f) / 7.5f;   float dv = (v - 7.5f) / 7.5f;   float dw = (w - 7.5f) / 7.5f;   float scale = sqrt(du * du + dv * dv + dw * dw);
   if(scale > 1.0f)    scale = 0.0f;   else    scale = 1.0f - scale;
   DWORD r = (DWORD)((w << 4) * scale);   DWORD g = (DWORD)((v << 4) * scale);   DWORD b = (DWORD)((u << 4) * scale);
   ((DWORD*) locked_box.pBits)[u] = 0xFF000000 + (r << 16) + (g << 8) + b;  }
  locked_box.pBits = (BYTE*) locked_box.pBits + locked_box.RowPitch; }
 locked_box.pBits = slice_start + locked_box.SlicePitch;}
g_volume_texture->UnlockBox(0);
还必须对立体纹理缓冲区进行解锁,这需要调用IDirect3DVolumeTexture9::UnlockBox(),代码如上所示,该函数声明如下:

Unlocks a box on a volume texture resource.

HRESULT UnlockBox(  UINT Level);
Parameters
Level
[in] Specifies the level of the volume texture resource to unlock.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.

为了生成立体纹理,还必须改变纹理的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);
 for(int i = 0; i < 4; i++)  ptr[i].w = sin(angle) * sin(angle);
 g_vertex_buffer->Unlock();}
最后设置要渲染的纹理层和立体纹理,纹理阶段混合方法,顶点数据,并渲染它:

// set textue and stream source and vertex formatpd3dDevice->SetTexture(0, g_volume_texture);pd3dDevice->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));pd3dDevice->SetFVF(D3DFVF_CUSTOM_VERTEX);
// set texture color blend methodpd3dDevice->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 sceneif( SUCCEEDED( pd3dDevice->BeginScene() ) ){ pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); RenderText();
 V(g_button_dlg.OnRender(fElapsedTime));    V( pd3dDevice->EndScene() );}
运行效果图:

按此在新窗口浏览图片

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

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

Creates a volume texture from a file.

HRESULT D3DXCreateVolumeTextureFromFile(  LPDIRECT3DDEVICE9 pDevice,  LPCTSTR pSrcFile,  LPDIRECT3DVOLUMETEXTURE9 * ppVolumeTexture);
Parameters
pDevice
[in] Pointer to an IDirect3DDevice9 interface, representing the device to be associated with the volume texture.
pSrcFile
[in] Pointer to a string that specifies the file name. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks.
ppVolumeTexture
[out] Address of a pointer to an IDirect3DVolumeTexture9 interface representing the created texture object.
Return Values
If the function succeeds, the return value is D3D_OK. If the function fails, the return value can be one of the following:

D3DERR_NOTAVAILABLE
D3DERR_OUTOFVIDEOMEMORY
D3DERR_INVALIDCALL
D3DXERR_INVALIDDATAE_OUTOFMEMORY
Remarks
The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateVolumeTextureFromFileW. Otherwise, the function call resolves to D3DXCreateVolumeTextureFromFileA because ANSI strings are being used.

The function is equivalent to D3DXCreateVolumeTextureFromFileEx(pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, ppVolumeTexture).

This function supports the following file formats: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, and .tga. See D3DXIMAGE_FILEFORMAT.

Mipmapped textures automatically have each level filled with the loaded texture.

When loading images into mipmapped textures, some devices are unable to go to a 1x1 image and this function will fail. If this happens, then the images need to be loaded manually.

Note that a resource created with this function will be placed in the memory class denoted by D3DPOOL_MANAGED.

Filtering is automatically applied to a texture created using this method. The filtering is equivalent to D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER in D3DX_FILTER.

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


--  作者:卷积内核
--  发布时间:12/10/2008 8:26:00 AM

--  
主程序:


#include "dxstdafx.h"
#include "resource.h"

#pragma warning(disable : 4127 4995 4996)

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

#define IDC_TOGGLE_FULLSCREEN        1
#define IDC_TOGGLE_REF                2
#define IDC_CHANGE_DEVICE            3

struct sCustomVertex
{
    float x, y, z;
    float u, v, w;
};

#define D3DFVF_CUSTOM_VERTEX    (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0))

const D3DXCOLOR FONT_COLOR(1.0f, 0.5f, 0.25f, 1.0f);

ID3DXFont*                    g_font;
ID3DXSprite*                g_text_sprite;
bool                        g_show_help;

CDXUTDialogResourceManager    g_dlg_resource_manager;
CD3DSettingsDlg                g_settings_dlg;
CDXUTDialog                    g_button_dlg;

IDirect3DVertexBuffer9*        g_vertex_buffer;
IDirect3DVolumeTexture9*    g_volume_texture;

//--------------------------------------------------------------------------------------
// Rejects any devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
                                  D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
    // Typically want to skip backbuffer formats that don't support alpha blending

    IDirect3D9* pD3D = DXUTGetD3DObject();

    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat,
                    D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

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

    return true;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed.
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext )
{
    // If video card does not support hardware vertex processing, then uses sofaware vertex processing.
    if((pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

    static bool is_first_time = true;

    if(is_first_time)
    {
        is_first_time = false;

        // if using reference device, then pop a warning message box.
        if(pDeviceSettings->DeviceType == D3DDEVTYPE_REF)
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}

//--------------------------------------------------------------------------------------
// Create volume texture and fill data.
//--------------------------------------------------------------------------------------
HRESULT CreateVolumeTexture(IDirect3DDevice9* device)
{
    HRESULT hr;
    UINT width = 16, height = 16, depth = 16;

    V_RETURN(device->CreateVolumeTexture(width, height, depth, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                         &g_volume_texture, NULL));

    D3DLOCKED_BOX locked_box;
    g_volume_texture->LockBox(0, &locked_box, NULL, 0);

    for(UINT w = 0; w < depth; w++)
    {
        BYTE* slice_start = (BYTE*) locked_box.pBits;

        for(UINT v = 0; v < height; v++)
        {
            for(UINT u = 0; u < width; u++)
            {
                // create texel color and fill it

                float du = (u - 7.5f) / 7.5f;
                float dv = (v - 7.5f) / 7.5f;
                float dw = (w - 7.5f) / 7.5f;
                float scale = sqrt(du * du + dv * dv + dw * dw);

                if(scale > 1.0f)
                    scale = 0.0f;
                else
                    scale = 1.0f - scale;

                DWORD r = (DWORD)((w << 4) * scale);
                DWORD g = (DWORD)((v << 4) * scale);
                DWORD b = (DWORD)((u << 4) * scale);

                ((DWORD*) locked_box.pBits)[u] = 0xFF000000 + (r << 16) + (g << 8) + b;
            }

            locked_box.pBits = (BYTE*) locked_box.pBits + locked_box.RowPitch;
        }

        locked_box.pBits = slice_start + locked_box.SlicePitch;
    }

    g_volume_texture->UnlockBox(0);

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Create any D3DPOOL_MANAGED resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice,
                                 const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                 void* pUserContext )
{
    HRESULT    hr;

    V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice));
    V_RETURN(g_settings_dlg.OnCreateDevice(pd3dDevice));

    D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
                   DEFAULT_PITCH | FF_DONTCARE, L"Arial", &g_font);
    
    // 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();

    return CreateVolumeTexture(pd3dDevice);
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_DEFAULT resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice,
                                const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                void* pUserContext )
{
    HRESULT hr;

    V_RETURN(g_dlg_resource_manager.OnResetDevice());
    V_RETURN(g_settings_dlg.OnResetDevice());
    V_RETURN(g_font->OnResetDevice());
    V_RETURN(D3DXCreateSprite(pd3dDevice, &g_text_sprite));

    // set dialog position and size

    g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0);
    g_button_dlg.SetSize(170, 170);

    // setup world matrix
    D3DXMATRIX mat_world;
    D3DXMatrixIdentity(&mat_world);
    pd3dDevice->SetTransform(D3DTS_WORLD, &mat_world);

    // setup view matrix

    D3DXMATRIX mat_view;
    D3DXVECTOR3 eye(0.0f, 0.0f, -8.0f);
    D3DXVECTOR3  at(0.0f, 0.0f,  0.0f);
    D3DXVECTOR3  up(0.0f, 1.0f,  0.0f);

    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    pd3dDevice->SetTransform(D3DTS_VIEW, &mat_view);

    // set projection matrix
    D3DXMATRIX mat_proj;
    float aspect = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, aspect, 1.0f, 100.0f);
    pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_proj);

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

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Release resources created in the OnResetDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnLostDevice();
    g_settings_dlg.OnLostDevice();
    g_font->OnLostDevice();

    release_com(g_text_sprite);
}


//--------------------------------------------------------------------------------------
// Release resources created in the OnCreateDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnDestroyDevice();
    g_settings_dlg.OnDestroyDevice();    

    release_com(g_font);
    release_com(g_vertex_buffer);
    release_com(g_volume_texture);
}

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

    for(int i = 0; i < 4; i++)
        ptr[i].w = sin(angle) * sin(angle);

    g_vertex_buffer->Unlock();
}

//--------------------------------------------------------------------------------------
// Render the helper information
//--------------------------------------------------------------------------------------
void RenderText()
{
    CDXUTTextHelper text_helper(g_font, g_text_sprite, 20);
    
    text_helper.Begin();

    // show frame and device states
    text_helper.SetInsertionPos(5, 5);
    text_helper.SetForegroundColor(FONT_COLOR);
    text_helper.DrawTextLine( DXUTGetFrameStats(true) );
    text_helper.DrawTextLine( DXUTGetDeviceStats() );

    // show helper information
    
    const D3DSURFACE_DESC* surface_desc = DXUTGetBackBufferSurfaceDesc();

    if(g_show_help)
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 18 * 5);
        text_helper.SetForegroundColor(FONT_COLOR);
        text_helper.DrawTextLine(L"Controls (F1 to hide):");
        
        text_helper.SetInsertionPos(40, surface_desc->Height - 18 * 4);
        text_helper.DrawTextLine(L"Quit: ESC");
    }
    else
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 4);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) );
        text_helper.DrawTextLine(L"Press F1 for help");
    }

    text_helper.End();
}

//--------------------------------------------------------------------------------------
// Render the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.OnRender(fElapsedTime);
        return;
    }

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


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
                          bool* pbNoFurtherProcessing, void* pUserContext )
{
    *pbNoFurtherProcessing = g_dlg_resource_manager.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
        return 0;
    }

    *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    return 0;
}


//--------------------------------------------------------------------------------------
// Handle keybaord event
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboardProc(UINT charater, bool is_key_down, bool is_alt_down, void* user_context)
{
    if(is_key_down)
    {
        switch(charater)
        {
        case VK_F1:
            g_show_help = !g_show_help;
            break;
        }
    }
}

//--------------------------------------------------------------------------------------
// Handle events for controls
//--------------------------------------------------------------------------------------
void CALLBACK OnGUIEvent(UINT event, int control_id, CDXUTControl* control, void* user_context)
{
    switch(control_id)
    {
    case IDC_TOGGLE_FULLSCREEN:
        DXUTToggleFullScreen();
        break;

    case IDC_TOGGLE_REF:
        DXUTToggleREF();
        break;

    case IDC_CHANGE_DEVICE:
        g_settings_dlg.SetActive(true);
        break;
    }
}

//--------------------------------------------------------------------------------------
// Initialize dialogs
//--------------------------------------------------------------------------------------
void InitDialogs()
{
    g_settings_dlg.Init(&g_dlg_resource_manager);
    g_button_dlg.Init(&g_dlg_resource_manager);

    g_button_dlg.SetCallback(OnGUIEvent);

    int x = 35, y = 10, width = 125, height = 22;

    g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y,         width, height);
    g_button_dlg.AddButton(IDC_TOGGLE_REF,          L"Toggle REF (F3)",     x, y += 24, width, height);
    g_button_dlg.AddButton(IDC_CHANGE_DEVICE,      L"Change device (F2)", x, y += 24, width, height, VK_F2);    
}

//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // Set the callback functions
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard(OnKeyboardProc);
   
    // TODO: Perform any application-level initialization here
    InitDialogs();

    // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"Create Volume Texture" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, IsDeviceAcceptable, ModifyDeviceSettings );

    // Start the render loop
    DXUTMainLoop();

    // TODO: Perform any application-level cleanup here

    return DXUTGetExitCode();
}

[B][URL=http://www.cppblog.com/Files/changingnow/VolumeTexture.rar]下载示例工程[/URL][/B]


--  作者:卷积内核
--  发布时间:12/10/2008 8:27:00 AM

--  
要渲染看起来真实的场景,最好是使用高分辨率而且颜色丰富的纹理,但这样的纹理可能会耗费大量的内存,例如,一张每像素16位颜色的256 x 256纹理将使用128KB的内存。如果在该纹理中使用多级渐进纹理,还需要额外的43KB内存。一个使用50张这种纹理的场景将需要8MB的内存,如果需要更强的真实性,可以使用每像素32位颜色的512 x 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。

DXT1格式的压缩比例是4 : 1(4x4块16位RGB纹理元素可压缩为64位,2个16位RGB565值和16个2位索引),这样的压缩比并不很高,但足以有效地将3D加速卡用于存储纹理的容量提高4倍,如下图所示:

按此在新窗口浏览图片

Direct3D支持5中DXT格式压缩:

FOURCC Description Alpha premultiplied?
DXT1 Opaque/1-bit alpha N/A
DXT2 Explicit alpha Yes
DXT3 Explicit alpha No
DXT4 Interpolated alpha Yes
DXT5 Interpolated alpha No

使用DXT压缩纹理

使用DXT压缩纹理前,必须先调用IDirect3D9::CheckDeviceFormat(),将表面格式设置为D3DFMT_DXT1 ~ D3DFMT_DXT5中的任意值,查询当前设备是否支持DXT压缩纹理:

// check whether device support DXT compressed textureif(FAILED(pD3D->CheckDeviceFormat(pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, 0, D3DRTYPE_TEXTURE,            D3DFMT_DXT1))) return false;
如果当前设备支持DXT压缩纹理,就可以直接创建DXT压缩纹理,并可以通过IDirect3DDevice9::SetTexture()函数设置DXT压缩纹理,将其映射到物体表面。如果当前设备不支持DXT压缩纹理,仍然可以DXT压缩格式存储纹理,但在使用该纹理渲染图形前,必须将DXT压缩格式的纹理转换为当前设备支持的格式。

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

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

可以通过DirectX SDK提供的纹理编辑工具"DirectX Texture Editor",创建压缩格式的纹理文件(后缀为DDS的纹理文件),或将其他格式的纹理文件转换为压缩格式的纹理文件。

在创建好压缩纹理之后,就可以和前面一样设置纹理及各项纹理渲染状态,最后进行渲染。如果当前设备支持DXT压缩纹理,那么在使用DXT纹理渲染图形时,Direct3D引擎会自动进行纹理压缩,如果当前设备不支持DXT压缩纹理,这时使用DXT压缩纹理和使用普通的非压缩纹理完全相同。

当使用大量纹理贴图渲染复杂场景时,通过使用DXT压缩纹理可以有效提高程序运行性能。

纹理管理

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

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

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

Assigns the resource-management priority for this resource.

DWORD SetPriority(  DWORD PriorityNew);
Parameters
PriorityNew
[in] DWORD value that specifies the new resource-management priority for the resource.
Return Values
Returns the previous priority value for the resource.

Remarks
SetPriority is used for priority control of managed resources. This method returns 0 on non-managed resources.

Priorities are used to determine when managed resources are to be removed from memory. A resource assigned a low priority is removed before a resource with a high priority. If two resources have the same priority, the resource that was used more recently is kept in memory; the other resource is removed. Managed resources have a default priority of 0.

原图:

按此在新窗口浏览图片

DXT压缩后:

按此在新窗口浏览图片


--  作者:卷积内核
--  发布时间:12/10/2008 8:27:00 AM

--  
主程序:


#include "dxstdafx.h"
#include "resource.h"

#pragma warning(disable : 4127)

#define IDC_TOGGLE_FULLSCREEN        1
#define IDC_TOGGLE_REF                2
#define IDC_CHANGE_DEVICE            3

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

struct sCustomVertex
{
    float x, y, z;
    float u, v;
};

#define D3DFVF_CUSTOM_VERTEX    (D3DFVF_XYZ | D3DFVF_TEX1)

ID3DXFont*                    g_font;
ID3DXSprite*                g_text_sprite;
bool                        g_show_help;

CDXUTDialogResourceManager    g_dlg_resource_manager;
CD3DSettingsDlg                g_settings_dlg;
CDXUTDialog                    g_button_dlg;

IDirect3DVertexBuffer9*        g_vertex_buffer;
IDirect3DTexture9*            g_texture;

//--------------------------------------------------------------------------------------
// Rejects any devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
                                  D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
    // Typically want to skip backbuffer formats that don't support alpha blending

    IDirect3D9* pD3D = DXUTGetD3DObject();

    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat,
                    D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

    // check whether device support DXT compressed texture
    if(FAILED(pD3D->CheckDeviceFormat(pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, 0, D3DRTYPE_TEXTURE,
                                       D3DFMT_DXT1)))
        return false;

    return true;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed.
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext )
{
    // If video card does not support hardware vertex processing, then uses sofaware vertex processing.
    if((pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

    static bool is_first_time = true;

    if(is_first_time)
    {
        is_first_time = false;

        // if using reference device, then pop a warning message box.
        if(pDeviceSettings->DeviceType == D3DDEVTYPE_REF)
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_MANAGED resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice,
                                 const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                 void* pUserContext )
{
    HRESULT    hr;

    V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice));
    V_RETURN(g_settings_dlg.OnCreateDevice(pd3dDevice));

    D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
                   DEFAULT_PITCH | FF_DONTCARE, L"Arial", &g_font);

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

    // create vertex buffer and fill data

    sCustomVertex vertices[] =     
    {
        { -3,   -3,  0.0f,  0.0f, 1.0f },   
        { -3,    3,  0.0f,  0.0f, 0.0f },    
        {  3,   -3,  0.0f,  1.0f, 1.0f },    
        {  3,    3,  0.0f,  1.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();

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_DEFAULT resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice,
                                const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                void* pUserContext )
{
    HRESULT hr;

    V_RETURN(g_dlg_resource_manager.OnResetDevice());
    V_RETURN(g_settings_dlg.OnResetDevice());
    V_RETURN(g_font->OnResetDevice());
    V_RETURN(D3DXCreateSprite(pd3dDevice, &g_text_sprite));

    // set dialog position and size

    g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0);
    g_button_dlg.SetSize(170, 170);

    // setup view matrix

    D3DXMATRIX mat_view;
    D3DXVECTOR3 eye(0.0f, 0.0f, -8.0f);
    D3DXVECTOR3  at(0.0f, 0.0f,  0.0f);
    D3DXVECTOR3  up(0.0f, 1.0f,  0.0f);

    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    pd3dDevice->SetTransform(D3DTS_VIEW, &mat_view);

    // set projection matrix
    D3DXMATRIX mat_proj;
    float aspect = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, aspect, 1.0f, 100.0f);
    pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_proj);

    // set texture and texture stage state and sample state
    pd3dDevice->SetTexture(0, g_texture);
    pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1,    D3DTA_TEXTURE);
    pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP,        D3DTOP_SELECTARG1);
    pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP,        D3DTOP_DISABLE);    
    pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);    

    pd3dDevice->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));
    pd3dDevice->SetFVF(D3DFVF_CUSTOM_VERTEX);

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Release resources created in the OnResetDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnLostDevice();
    g_settings_dlg.OnLostDevice();
    g_font->OnLostDevice();

    release_com(g_text_sprite);
}


//--------------------------------------------------------------------------------------
// Release resources created in the OnCreateDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnDestroyDevice();
    g_settings_dlg.OnDestroyDevice();    

    release_com(g_font);
    release_com(g_vertex_buffer);
    release_com(g_texture);
}

//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
}

//--------------------------------------------------------------------------------------
// Render the helper information
//--------------------------------------------------------------------------------------
void RenderText()
{
    CDXUTTextHelper text_helper(g_font, g_text_sprite, 20);
    
    text_helper.Begin();

    // show frame and device states
    text_helper.SetInsertionPos(5, 5);
    text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 0.475f, 0.0f, 1.0f) );
    text_helper.DrawTextLine( DXUTGetFrameStats(true) );
    text_helper.DrawTextLine( DXUTGetDeviceStats() );

    // show helper information
    
    const D3DSURFACE_DESC* surface_desc = DXUTGetBackBufferSurfaceDesc();

    if(g_show_help)
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 6);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 0.475f, 0.0f, 1.0f) );
        text_helper.DrawTextLine(L"Controls (F1 to hide):");
        
        text_helper.SetInsertionPos(40, surface_desc->Height - 15 * 4);
        text_helper.DrawTextLine(L"Quir: ESC");
    }
    else
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 4);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) );
        text_helper.DrawTextLine(L"Press F1 for help");
    }

    text_helper.End();
}

//--------------------------------------------------------------------------------------
// Render the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.OnRender(fElapsedTime);
        return;
    }

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


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
                          bool* pbNoFurtherProcessing, void* pUserContext )
{
    *pbNoFurtherProcessing = g_dlg_resource_manager.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
        return 0;
    }

    *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    return 0;
}


//--------------------------------------------------------------------------------------
// Handle keybaord event
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboardProc(UINT charater, bool is_key_down, bool is_alt_down, void* user_context)
{
    if(is_key_down)
    {
        switch(charater)
        {
        case VK_F1:
            g_show_help = !g_show_help;
            break;
        }
    }
}

//--------------------------------------------------------------------------------------
// Handle events for controls
//--------------------------------------------------------------------------------------
void CALLBACK OnGUIEvent(UINT event, int control_id, CDXUTControl* control, void* user_context)
{
    switch(control_id)
    {
    case IDC_TOGGLE_FULLSCREEN:
        DXUTToggleFullScreen();
        break;

    case IDC_TOGGLE_REF:
        DXUTToggleREF();
        break;

    case IDC_CHANGE_DEVICE:
        g_settings_dlg.SetActive(true);
        break;
    }
}

//--------------------------------------------------------------------------------------
// Initialize dialogs
//--------------------------------------------------------------------------------------
void InitDialogs()
{
    g_settings_dlg.Init(&g_dlg_resource_manager);
    g_button_dlg.Init(&g_dlg_resource_manager);

    g_button_dlg.SetCallback(OnGUIEvent);

    int x = 35, y = 10, width = 125, height = 22;

    g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y,         width, height);
    g_button_dlg.AddButton(IDC_TOGGLE_REF,          L"Toggle REF (F3)",     x, y += 24, width, height);
    g_button_dlg.AddButton(IDC_CHANGE_DEVICE,      L"Change device (F2)", x, y += 24, width, height, VK_F2);    
}

//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // Set the callback functions
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard(OnKeyboardProc);
   
    // TODO: Perform any application-level initialization here
    InitDialogs();

    // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"TEX Compressed Texture" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 800, 600, IsDeviceAcceptable, ModifyDeviceSettings );

    // Start the render loop
    DXUTMainLoop();

    // TODO: Perform any application-level cleanup here

    return DXUTGetExitCode();
}

[B][URL=http://www.cppblog.com/Files/changingnow/DXTTexture.rar]下载示例工程[/URL][/B]


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
312.988ms