来源:http://www.cnblogs.com/fence/archive/2010/03/12/1683920.html
欢迎阅读 Driving DirectX。这个月,我将通过开发一个使用 alpha 的 Direct3D 屏幕保护程序继续对 alpha 混色进行探索。
图 1. MSDNSparkles 屏幕快照
图 1 显示了该示例的一个屏幕快照;要想真正欣赏这些图像,您必须花上一定的时间来观察屏幕保护程序的形态变化。
根据 Direct3D "ScreenSaver" 示例,MSDNSparkles 屏幕保护程序将一个粒子系统与一些随机的戏法结合起来;如果这种方式与 alpha 混色相结合,就会产生梦幻般的图像。这是一个使用 ONE:ONE 操作符进行加色的示例,这会产生一些确实有趣的发光效果。
在本专栏中,我将简要概括 Direct3D 屏幕保护示例的框架,然后深入介绍 MSDNSparkles 实现其戏法的细节。我不会涉及编写 Win32 屏幕保护程序的细节,但您可以在 MSDN 上搜索讨论这一主题的文章。
引言
Direct3D 屏幕保护程序的框架由两部分组成:骨架和应用程序提供的函数,这些函数由每个屏幕保护程序覆盖。图 2 显示了 SDK 的屏幕保护程序样本工程视图。Screensaver.cpp 提供了一个使 D3DFrame. 屏幕保护程序能够运行的骨架,如 d3dapp.cpp,而 d3dapp.cpp 反过来又提供了 D3DFrame. 应用程序的骨架。只要提供覆盖的函数就可以得到一个屏幕保护程序。请记住,如果要对屏幕保护程序进行测试,则需要使用一个命令行参数 –s 从 Visual C++ 中将其启动。
图 2. Direct3D 屏幕保护程序样本项目视图
该骨架使用了一些类似于那些 D3DFrame. 应用程序用于执行初始化、关闭和着色的函数。这些函数的原型—Initialize3DEnvironment、Cleanup3DEnvironment 和 Render3DEnvironment— 如下所示。
// --------------------------------------------------------------------------
// 局部函数原型
// ------------------------------------------------------------------------- -
HRESULT Initialize3DEnvironment( HWND );
HRESULT Render3DEnvironment( HWND );
VOID Cleanup3DEnvironment( HWND );
这三个函数将完成设置 Direct3D、关闭 Direct3D 和对场景进行着色的所有操作。屏幕保护程序必须完成的所有工作就是实现可以被覆盖的函数。这些函数类似于我们已经逐渐了解和喜爱的 D3Dframe. 对应函数;请参见下面的函数原型。
// --------------------------------------------------------------------------
// 外部函数原型
// --------------------------------------------------------------------------
HRESULT App_ConfirmDevice( DDCAPS * , D3DDEVICEDESC * );
HRESULT App_OneTimeSceneInit();
VOID App_DeleteDeviceObjects( HWND, LPDIRECT3DDEVICE7 );
HRESULT App_InitDeviceObjects( HWND, LPDIRECT3DDEVICE7 );
HRESULT App_FrameMove( LPDIRECT3DDEVICE7, FLOAT );
HRESULT App_Render( LPDIRECT3DDEVICE7 );
HRESULT App_RestoreSurfaces();
HRESULT App_FinalCleanup();
我使用了示例中的 screensaver.cpp,并做了最少的改动。我的确发现,从注册表中读取配置数据的调用需要将参数由:
if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, strRegPath, KEY_READ, NULL, & hKey ) )
调换为:
if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, strRegPath, NULL, KEY_READ, & hKey ) )
该示例也未能对着色窗口可能的移动进行处理,因此我在调用 TestCooperativeLevel() 之后把以下程序块加入了 Render3Denvironment() 函数。
// 请确保在屏幕控制面板中进行操作,监视事物的移动
RECT rTmp;
GetWindowRect( hWnd, & rTmp );
g_pFramework -> Move(rTmp.left, rTmp.top );
Direct3D 屏幕保护程序框架确实给出了 ConfigureDialog 函数的开始部分以及一种用于存储配置数据的方法,但我尚未针对 MSDNSparkles 对其进行扩充,因为它不是一个关键细节。
样例屏幕保护程序的另一个“特征”是:它使用 WM_TIMER 对着色进行控制。在基于 Windows 2000 的操作系统上,这会产生一个适当的帧速率,但基于 Windows9x 的操作系统并不具有这样一个允许使用良好帧速率的 WM_TIMER 间隔尺寸。考虑到这种情况,我重新编写了这个屏幕保护程序,使其使用线程对着色进行控制。我没有将这部分代码包括进来,因为它是一个简单明了的线程实现。 有兴趣的程序员可以考虑增加多监视器功能、电源管理和口令,因为 Direct3D 屏幕保护程序框架没有提供这些功能。
高级别视图
既然这些细节已不成为问题,我们就可以开始了。图 3 显示了 Visual Studio Workspace 窗口针对 MSDNSparkles 示例的内容。该项目由 screensaver.cpp 文件(它是从 Direct3D 示例中得到的,如上面的讨论所述)以及 sparkles.cpp 组成。
图 3. MSDNSparkles项目视图
Sparkles.cpp 包含两组函数。第一组是可以覆盖的函数,我会在后面对其进行考查。第二组包含了闪烁效果函数。为了对闪烁效果函数进行处理,MSDNSparkles 定义了它自己的小型 API,它们是 RandomTexture、RandomSparkle、InitSparkles、UpdateSparkle 和 DrawSparkle。
// 闪烁效果函数
Int RandomTexture( int overflow);
Sparkle RandomSparkle( void );
Void InitSparkles( void );
Void UpdateSparkles( void );
BOOL DrawSparkles(LPDIRECT3DDEVICE7 lpDev, D3DVECTOR from, D3DVECTOR at);
除了 RandomTexture 以外,所有这些函数都对 Sparkle 结构进行操作。Sparkle 结构包含用于每个粒子的外观和行为的信息,而每一个粒子都作为粒子系统的一部分。
// 闪烁效果的结构
typedef struct t_sparkle {
int texture;
int age, cur_age; // start at age and tick down to 0
float scale, delta_scale;
D3DVECTOR position;
D3DVECTOR velocity;
D3DVECTOR color;
} Sparkle;
元素 position 和 velocity,随同 scale 和 delta_scale 一起由粒子系统使用,用于生成粒子的各个位置。元素 texture 与 color 定义作为外观随机化算法一部分的粒子外观。
外观随机化通过使用一个衰老系统对转换进行控制,其中,元素 age 与 cur_age
给出了一个倒计时系统。转换发生的方式有三种:
这些随机纹理和色彩随后会对粒子的外观进行完整定义。某些精巧的随机数学方法所生成的内容确实相当奇妙。
RandomTexture 用来帮助从纹理列表中选择一种纹理。
int RandomTexture( int texture, int overflow)
{
int retVal;
if (texture == NumTextures)
{
retVal = overflow; // init to random from the n case, overflow
}
else
{
retVal = texture; // init to current in the 0..n-1 case
}
return retVal;
}
RandomSparkle 通过生成一些颜色增量值开始。它随后开始着手置入一个 Sparkles 结构,该结构用时限、标度和位置值来表示粒子。闪烁效果的纹理是通过调用 RandomTexture 选择的。随后,color_mode 和 color_modifier_mode 被用来生成该粒子的颜色。
Color_mode 确定哪一种方法被用于生成基本颜色,随机模式还是 rgb 颤动模式。随机模式只是随机地选取一种颜色。而 RGB 颤动模式则进行一些精巧的颜色增量计算,在某一颜色范围内平滑地移动。Color_modifier_mode 控制着基色的修改。可以选择四种变量:饱和色、宝石色、柔和色或亮色。这些模式中的每一种都执行一种略微不同的计算,借以对基色进行修改。
Sparkle RandomSparkle( void )
{
Sparkle ret;
static float red = 1.0f , grn = 1.0f , blu = 1.0f ;
static float d_red = - (min_color_delta + rnd() * max_color_delta);
static float d_grn = - (min_color_delta + rnd() * max_color_delta);
static float d_blu = - (min_color_delta + rnd() * max_color_delta);
ret.age = min_age + ( int )(rnd() * (max_age - min_age));
ret.cur_age = ret.age;
ret.scale = start_scale;
ret.delta_scale = min_delta + rnd() * (max_delta - min_delta);
ret.position = D3DVECTOR(world_size * (rnd() - rnd()), world_size * (rnd() - rnd()), world_size * (rnd() - rnd()));
ret.velocity = D3DVECTOR( 0.0f );
ret.texture = RandomTexture(rand() % (NumTextures - 1 ));
switch (color_mode)
{
case 0 : // 随机
ret.color = D3DVECTOR(rnd(), rnd(), rnd());
break ;
case 1 : // rgb 颤动
red += d_red;
if (red > 1.0f )
{
red = 1.0f ;
d_red = - (min_color_delta + rnd() * max_color_delta);
}
else if (red < 0.0f )
{
red = 0.0f ;
d_red = min_color_delta + rnd() * max_color_delta;
}
grn += d_grn;
if (grn > 1.0f )
{
grn = 1.0f ;
d_grn = - (min_color_delta + rnd() * max_color_delta);
}
else if (grn < 0.0f )
{
grn = 0.0f ;
d_grn = min_color_delta + rnd() * max_color_delta;
}
blu += d_blu;
if (blu > 1.0f )
{
blu = 1.0f ;
d_blu = - (min_color_delta + rnd() * max_color_delta);
}
else if (blu < 0.0f )
{
blu = 0.0f ;
d_blu = min_color_delta + rnd() * max_color_delta;
}
ret.color = D3DVECTOR(red, grn, blu);
break ;
default :
ret.color = D3DVECTOR( 0.0f , 0.5f , 1.0f );
break ;
}
switch (color_modifier_mode)
{
case 0 : // 无变化
break ;
case 1 : // 饱和色
ret.color /= Max(ret.color);
break ;
case 2 : // 宝石色
ret.color -= Min(ret.color);
ret.color /= Max(ret.color);
break ;
case 3 : // 柔和色
ret.color -= Min(ret.color);
ret.color /= Max(ret.color);
ret.color = D3DVECTOR( 0.6f ) + 0.4f * ret.color;
break ;
case 4 : // 亮色和冷色,并可用于大多数情况
ret.color *= 1.2f ;
break ;
default :
break ;
}
return ret;
}
InitSparkles 选择起始纹理,然后为粒子列表分配内存。一个粒子基本上是由两个三角形构成的四边形。对粒子的说明,请参见图 4。
图 4. 粒子
接下来,每个闪烁效果都通过 RandomSparkle 被随机地初始化。最后,为每个粒子生成索引绘图的下标。
void InitSparkles( void )
{
texture = 1 ; // 以 dx7 位图开始
sparkle = (Sparkle * )malloc(nMaxNumSparkles * sizeof (Sparkle));
for (UINT i = 0 ; i < nCurNumSparkles; i ++ )
{
sparkle[i] = RandomSparkle();
}
// 设置下标
for (i = 0 ; i < nMaxNumSparkles; i ++ )
{
s_indices[i * 6 + 0 ] = 4 * i + 0 ;
s_indices[i * 6 + 1 ] = 4 * i + 1 ;
s_indices[i * 6 + 2 ] = 4 * i + 2 ;
s_indices[i * 6 + 3 ] = 4 * i + 0 ;
s_indices[i * 6 + 4 ] = 4 * i + 2 ;
s_indices[i * 6 + 5 ] = 4 * i + 3 ;
}
} // InitSparkles() 结束
UpdateSparkles 将 texture_age、color_age 和 color_modifier_age 的当前值递减。随后,如果时限值已经倒数至 0,则会生成 texture (使用 RandomTexture)、color_mode 以及 color_modifier_mode 的新的随机值。此操作一旦完成,RandomSparkle 将被再次用来随机生成每个粒子下一帧的位置、纹理和颜色。最后对标度进行调整。
void UpdateSparkles( void )
{
UINT i;
// 0..n 0..n-1==当前,n==随机
texture_age -- ;
if (texture_age == 0 )
{
texture_age = min_texture_age + (unsigned int )(rnd() * (max_texture_age - min_texture_age));
texture = rand() % (NumTextures);
texture = RandomTexture(rand() % (NumTextures - 1 ));
}
// 0..1 0==随机,1==rgb 颤动
color_age -- ;
if (color_age == 0 )
{
color_age = min_color_age + ( int )(rnd() * (max_color_age – min_color_age));
color_mode = rand() % NumColorModes;
}
// 0..5 0==无变化,1==饱和色,2==宝石色,3==柔和色,
// 4==亮色,5==暗色和冷色,但不适用于大多数情况,
color_modifier_age -- ;
if (color_modifier_age == 0 )
{
color_modifier_age = min_color_modifier_age + ( int )(rnd() * (max_color_modifier_age - min_color_modifier_age));
color_modifier_mode = rand() % NumColorModifierModes;
}
// 更新闪烁效果
for (i = 0 ; i < nCurNumSparkles; i ++ )
{
sparkle[i].cur_age -- ;
if (sparkle[i].cur_age == 0 )
{
sparkle[i] = RandomSparkle();
}
sparkle[i].scale *= sparkle[i].delta_scale;
}
} // UpdateSparkles() 结束
DrawSparkles 对粒子系统定位代码中使用的前面方向进行计算。该值被用来生成每个粒子各顶点的偏移量。图 5 对此进行了说明。
图 5. 粒子定位
为了提高效率,闪烁效果按绘制的纹理进行排序。索引列表及 DrawIndexedPrimitive 用来绘制粒子系统的四边形。
BOOL DrawSparkles(LPDIRECT3DDEVICE7 lpDev, D3DVECTOR from, D3DVECTOR at)
{
D3DVECTOR view_dir, position, dx, dy;
UINT i;
view_dir = Normalize(at - from);
dx = CrossProduct(view_dir, D3DVECTOR( 0.0f , 1.0f , 0.0f ));
dy = CrossProduct(view_dir, dx);
dx = CrossProduct(view_dir, dy);
// 绘制闪烁效果
// 为提高效率,我们要将所有使用相同纹理的闪烁效果
// 批量处理并只进行一次 DrawPrim 调用
int flags[NumTextures];
for ( int tex = 0 ; tex < NumTextures; tex ++ )
{
flags[tex] = 0 ;
}
// 计算出正在使用哪些纹理
for (i = 0 ; i < nCurNumSparkles; i ++ )
{
flags[sparkle[i].texture] ++ ;
}
// 对于每种使用的纹理,批量处理闪烁效果并进行绘制
for (tex = 0 ; tex < NumTextures; tex ++ )
{
if (flags[tex] == 0 )
continue ;
// 设置正确的材质/纹理组合
lpDev -> SetTexture( 0 ,g_ptexSparkleTextures[tex]);
// 建造用于批处理的四边形
int num = 0 ;
for (i = 0 ; i < nCurNumSparkles; i ++ )
{
if (sparkle[i].texture != tex)
continue ;
D3DVECTOR sx = dx * sparkle[i].scale;
D3DVECTOR sy = dy * sparkle[i].scale;
float color_scale = ( float )sparkle[i].cur_age / sparkle[i].age;
D3DVECTOR cur_color = sparkle[i].color * color_scale;
D3DCOLOR color = D3DRGB(cur_color[ 0 ], cur_color[ 1 ], cur_color[ 2 ]);
position = sparkle[i].position;
s_mesh[num * 4 + 0 ] = D3DLVERTEX(position + sx + sy, color, 0 , 1.0f , 1.0f );
s_mesh[num * 4 + 1 ] = D3DLVERTEX(position - sx + sy, color, 0 , 0.0f , 1.0f );
s_mesh[num * 4 + 2 ] = D3DLVERTEX(position - sx - sy, color, 0 , 0.0f , 0.0f );
s_mesh[num * 4 + 3 ] = D3DLVERTEX(position + sx - sy, color, 0 , 1.0f , 0.0f );
num ++ ;
}
// 已完成批处理的创建,现在进行着色
if (lpDev -> DrawIndexedPrimitive(D3DPT_TRIANGLELIST, D3DFVF_LVERTEX, (LPVOID)s_mesh,
4 * flags[tex], s_indices, 6 * flags[tex], 0 ) != D3D_OK)
return FALSE;
}
return TRUE;
} // DrawSparkles() 结束
图 6 和图 7 显示了由 alpha 混色粒子系统产生的图像的附加屏幕快照。
图 6. MSDNSparkles 屏幕快照 2
图 7. MSDNSparkles 屏幕快照 3
MSDNSparkles 的内部构件
让我们继续探讨过载应用程序函数的实现过程。我在下面重新列出了函数的原型,这样您就不必回卷到本文开始的地方。我将对 Direct3D 屏幕保护程序框架允许覆盖的每个过载函数进行考查。
// --------------------------------------------------------------------------
// 外部函数原型
// --------------------------------------------------------------------------
HRESULT App_ConfirmDevice( DDCAPS * , D3DDEVICEDESC * );
HRESULT App_OneTimeSceneInit();
VOID App_DeleteDeviceObjects( HWND, LPDIRECT3DDEVICE7 );
HRESULT App_InitDeviceObjects( HWND, LPDIRECT3DDEVICE7 );
HRESULT App_FrameMove( LPDIRECT3DDEVICE7, FLOAT );
HRESULT App_Render( LPDIRECT3DDEVICE7 );
HRESULT App_RestoreSurfaces();
HRESULT App_FinalCleanup();
ConfirmDevice
MSDNSparkles App_ConfirmDevice 函数验证设备是否能够进行 ONE:ONE alpha 混色。这是 alpha 最简单的形式 — 并且,根据我的经验,所有的显卡都可以执行这种 alpha 混色。下面是 MSDNSparkles 实现 App_ConfirmDevice 的过程。
HRESULT App_ConfirmDevice( DDCAPS * pddDriverCaps,
D3DDEVICEDESC7 * pd3dDeviceDesc )
{
// 获取 triangle caps(硬件或软件),并检验 alpha 混色
LPD3DPRIMCAPS pdpc = & pd3dDeviceDesc -> dpcTriCaps;
if ( 0 == ( pdpc -> dwSrcBlendCaps & pdpc -> dwDestBlendCaps & D3DBLEND_ONE ) )
return E_FAIL;
return S_OK;
}
OneTimeSceneInit
App_OneTimeSceneInit 函数首先给随机数生成器播种,并设置背景颜色和初始化纹理列表。然后,它使用纹理列表创建 MSDNSparkles 使用的纹理。最后,调用 InitSparkles 对粒子系统进行初始化。
HRESULT App_OneTimeSceneInit()
{
// 给随机数生成器播种
srand(time( 0 ));
// 初始化背景颜色
bckColor = D3DRGB( 0 , 0 , 0 );
// 装载纹理数据
memcpy(g_szSparkleTextures[ 0 ], " dx5.bmp " , sizeof ( " dx5.bmp " ));
memcpy(g_szSparkleTextures[ 1 ], " dx7.bmp " , sizeof ( " dx7.bmp " ));
memcpy(g_szSparkleTextures[ 2 ], " flare1.bmp " , sizeof ( " flare1.bmp " ));
memcpy(g_szSparkleTextures[ 3 ], " flare2.bmp " , sizeof ( " flare2.bmp " ));
memcpy(g_szSparkleTextures[ 4 ], " flare3.bmp " , sizeof ( " flare3.bmp " ));
memcpy(g_szSparkleTextures[ 5 ], " flare4.bmp " , sizeof ( " flare5.bmp " ));
memcpy(g_szSparkleTextures[ 6 ], " flare5.bmp " , sizeof ( " flare5.bmp " ));
memcpy(g_szSparkleTextures[ 7 ], " flare6.bmp " , sizeof ( " flare6.bmp " ));
memcpy(g_szSparkleTextures[ 8 ], " flare7.bmp " , sizeof ( " flare7.bmp " ));
memcpy(g_szSparkleTextures[ 9 ], " flare8.bmp " , sizeof ( " flare8.bmp " ));
memcpy(g_szSparkleTextures[ 10 ], " shine1.bmp " , sizeof ( " flare1.bmp " ));
memcpy(g_szSparkleTextures[ 11 ], " shine2.bmp " , sizeof ( " flare2.bmp " ));
memcpy(g_szSparkleTextures[ 12 ], " shine3.bmp " , sizeof ( " flare3.bmp " ));
memcpy(g_szSparkleTextures[ 13 ], " shine4.bmp " , sizeof ( " flare5.bmp " ));
memcpy(g_szSparkleTextures[ 14 ], " shine5.bmp " , sizeof ( " flare5.bmp " ));
memcpy(g_szSparkleTextures[ 15 ], " shine6.bmp " , sizeof ( " flare6.bmp " ));
for ( int i = 0 ; i < NumTextures; i ++ )
D3DTextr_CreateTextureFromFile( ( char * )g_szSparkleTextures[i] );
InitSparkles();
return S_OK;
}
InitDeviceObjects
App_InitDeviceObjects 函数使用 helper 函数 SetTextureState、SetRenderState 和 SetViewState。这些函数分别对纹理列表、应用程序所使用的着色状态和查看系统进行设置。请注意,已启用了alpha 混色,混色状态将为加色而设置成 ONE:ONE。
void SetTextureState(LPDIRECT3DDEVICE7 pd3dDevice )
{
// 设置纹理状态
D3DTextr_RestoreAllTextures( pd3dDevice );
// 装载纹理表面
for ( int i = 0 ; i < NumTextures; i ++ )
g_ptexSparkleTextures[i] = D3DTextr_GetSurface( ( char * )g_szSparkleTextures[i]);
}
void SetRenderState(LPDIRECT3DDEVICE7 pd3dDevice )
{
// alpha 混色状态
pd3dDevice -> SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
pd3dDevice -> SetRenderState(D3DRENDERSTATE_SRCBLEND, srcBlend);
pd3dDevice -> SetRenderState(D3DRENDERSTATE_DESTBLEND, dstBlend);
// 过滤器状态
pd3dDevice -> SetTextureStageState( 0 ,D3DTSS_MINFILTER, D3DFILTER_LINEAR);
pd3dDevice -> SetTextureStageState( 0 ,D3DTSS_MAGFILTER, D3DFILTER_LINEAR);
pd3dDevice -> SetTextureStageState( 0 ,D3DTSS_MIPFILTER, D3DFILTER_LINEAR);
// 设置非纹理着色状态
pd3dDevice -> SetRenderState(D3DRENDERSTATE_DITHERENABLE, FALSE);
pd3dDevice -> SetRenderState(D3DRENDERSTATE_SPECULARENABLE, FALSE);
pd3dDevice -> SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE);
pd3dDevice -> SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE);
pd3dDevice -> SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
// 注:在 DX7 中,要关闭顶点照明,需要将 D3DRENDERSTATE_LIGHTING 设置为 FALSE
// (并改用 D3DLVERTEX 中的颜色)
pd3dDevice -> SetRenderState( D3DRENDERSTATE_LIGHTING, FALSE );
}
void SetViewState(LPDIRECT3DDEVICE7 pd3dDevice )
{
// 获取宽高比
pd3dDevice -> GetViewport( & vp);
FLOAT fAspect = ((FLOAT)vp.dwHeight) / vp.dwWidth;
// 设置变换矩阵
D3DUtil_SetProjectionMatrix( proj, g_PI / 4 , // 1.0f;
fAspect, 1.0f , MAX_DIST );
pd3dDevice -> SetTransform( D3DTRANSFORMSTATE_PROJECTION, & proj);
D3DUtil_SetViewMatrix( view, from, at, up );
pd3dDevice -> SetTransform( D3DTRANSFORMSTATE_VIEW, & view);
D3DUtil_SetIdentityMatrix( world );
pd3dDevice -> SetTransform( D3DTRANSFORMSTATE_WORLD, & world);
}
HRESULT App_InitDeviceObjects( HWND hWnd, LPDIRECT3DDEVICE7 pd3dDevice )
{
// 检查参数
if ( NULL == pd3dDevice )
return E_INVALIDARG;
// 设置纹理状态
SetTextureState( pd3dDevice );
// 设置着色状态
SetRenderState( pd3dDevice );
// 设置查看系统
SetViewState( pd3dDevice );
return S_OK;
}
FrameMove
App_FrameMove 函数使用 UpdateSparkles 对粒子系统模拟进行更新。
HRESULT App_FrameMove( LPDIRECT3DDEVICE7 pd3dDevice, FLOAT fTimeKey )
{
// 好,现在可以使用闪烁效果了
UpdateSparkles();
return S_OK;
}
着色
App_Render 函数相当简单。首先,我们使用时间和 RandomViewpoint 随时间的推移对视点进行修改。然后我们让 DrawSparkles 绘制粒子系统。太简单了!
void RandomViewpoint(LPDIRECT3DDEVICE7 pd3dDevice, float tic)
{
float fromX, fromY, fromZ;
fromX = ( float )sin(tic * 0.59 );
fromZ = ( float )cos(tic * 0.59 );
if ( texture <= 9 )
fromY = ( float )sin(tic * 0.72 );
else
fromY = ( float )cos(tic * 0.72 );
from = D3DVECTOR(orbit_size * fromX, orbit_size * fromY, orbit_size * fromZ );
D3DUtil_SetViewMatrix( view, from, at, up );
pd3dDevice -> SetTransform( D3DTRANSFORMSTATE_VIEW, & view);
}
HRESULT App_Render( LPDIRECT3DDEVICE7 pd3dDevice )
{
static float tic = - rnd() * 10000.0f ;
// 清除取景框
pd3dDevice -> Clear( 0UL , NULL, D3DCLEAR_TARGET, bckColor, 1.0f , 0L );
// 开始场景
if ( FAILED( pd3dDevice -> BeginScene() ) )
return S_OK; // Don't return a "fatal" error
// tic 来回移动物品
tic += 0.005f ;
// 在随机定位中使用
start_scale = 0.05f + ( float )(sin(tic * 0.100 ) + 1.0f ) * 0.4f ;
world_size = 0.10f + ( float )(cos(tic * 0.072 ) + 1.0f ) * 10.0f ;
// 修改取景框
RandomViewpoint(pd3dDevice,tic);
// 绘制闪烁效果
if ( ! DrawSparkles(pd3dDevice, from, at))
return E_FAIL;
// 结束场景
pd3dDevice -> EndScene();
return S_OK;
}
DeleteDeviceObjects
App_DeleteDeviceObjects 函数使纹理列表中的纹理失效。
VOID App_DeleteDeviceObjects( HWND hWnd, LPDIRECT3DDEVICE7 pd3dDevice)
{
D3DTextr_InvalidateAllTextures();
}
RestoreSurfaces
对于 MSDNSparkles 示例,App_RestoreSurfaces 为空操作。
HRESULT App_RestoreSurfaces()
{
return S_OK;
}
FinalCleanup
App_FinalCleanup 函数删除闪烁效果列表所使用的内存。
HRESULT App_FinalCleanup()
{
free(sparkle);
return S_OK;
}
MSDNSparkles 的实现过程到此结束。当您构建这个项目时,可以通过用鼠标右键单击 MSDNSparkles.scr 文件,对产生的屏幕保护程序进行测试、配置和安装。图 6 对此进行了说明。结果是基本的,但也不无快意。
图 8. 单击右键可对屏幕保护程序进行测试、配置和安装
结语
欢迎您反馈意见。欢迎通过下面的地址给我写信,请附上您的意见、问题、对专题的看法,或是您自己关于本专栏所涉及专题的观点变化的链接。尽管如此,请不要期待个别回信或向我发送需求技术支持的问题。请记住,Microsoft 保持着一个活跃的邮件列表 DirectXDev,它是一个志趣相投的开发人员共享信息的论坛。可以在 http://DISCUSS.MICROSOFT.COM/archives/DIRECTXDEV.html(英文)找到相应的 Web 界面。提问前请阅读 http://msdn.microsoft.com/library/default.asp%20URL=/library/techart/dxfaq2.htm(英文)上的常见问题解答。