Alpha Blinking Effect Philip Taylor
September 2000
Download the source code of this article (521 KB)
Welcome to DRIVING DIRECTX. This month, I will continue to explore the alpha color color by developing a Direct 3D screen saver using Alpha.
Figure 1. MSDNSPARKLES screen photo snapshot
Figure 1 shows a screen snapshot of the example; if you want to truly appreciate these images, you must spend a certain time to observe the morphological changes of the screen saver.
Based on the Direct3D "Screensaver" example, the MSDNSParkles screen saver combines a particle system with some random tricks; if this method is combined with Alpha mixing, a fantastic image is generated. This is an example of using an ONE: One operator to produce some exquisite luminescent effects.
In this column, I briefly summarize the framework of the Direct3D screen protection example, then introduce the details of MSDNSParkles to implement their tricks. I won't involve the details of the Win32 screen saver, but you can search for this topic on the MSDN.
introduction
The framework of the Direct3D screen saver consists of two parts: the function provided by the skeleton and the application, which is overwritten by each screen saver. Figure 2 shows the SDK screen saver sample engineering view. ScreenSaver.cpp provides a skeleton that makes the D3DFrame screen saver, such as d3dapp.cpp, and D3Dapp.cpp reverses the skeleton of the D3DFrame application. A screen saver can be obtained as long as the overlay function is provided. Keep in mind that if you want to test the screen saver, you need to use a command line parameter -s to start from Visual C .
Figure 2. Direct3D screen saver sample project view
The skeleton uses some functions similar to those D3DFrame applications for performing initialization, closing, and coloring. The prototype of these functions -Initialize3denvironment, Cleanup3denvironment and Render3Denvironment are as follows.
/ / -------------------------------------------------------------------------------------------- ----------------------------
// Local function prototype
/ / -------------------------------------------------------------------------------------------- ----------------------------
HRESULT INITIALIZE3DENVIRONMENT (HWND);
HRESULT RENDER3DENVIRONMENT (HWND);
Void Cleanup3denvironment (hwnd);
These three functions will complete the setting Direct3D, close Direct3D, and all the operations coloring the scene. All jobs that the screen saver must be completed is a function that can be overwritten. These functions are similar to we have gradually understood and loved D3DFrame corresponding functions; see the function prototype below.
/ / -------------------------------------------------------------------------------------------- ----------------------------
// External function prototype
/ / -------------------------------------------------------------------------------------------- ----------------------------
HRESULT APP_CONFIRMDEVICE (DDCAPS *, D3DDeviceDesc *);
HRESULT APP_ONETIMESCENINIT ();
Void app_deletedeviceObjects (hwnd, lpdirect3ddevice7);
HRESULT APP_INITDEVICEOBJECTS (HWND, LPDIRECT3DDEVICE7); HRESULT APP_FRAMEMOVE (LPDIRECT3DDEVICE7, FLOAT);
HRESULT APP_RENDER (LPDIRECT3DDEVICE7);
HRESULT APP_RESTORESURFCESURFCES ();
HRESULT APP_FINALCLANUP ();
I used ScreenSaver.cpp in the example and did the least change. I did find that the call from the registry needs to send the parameters:
IF (Error_Success == RegopenKeyex (HKEY_CURRENT_USER, STRREGPATH,
Key_Read, Null, & HKey))
Convert to:
IF (Error_Success == RegopenKeyex (HKEY_CURRENT_USER, STRREGPATH,
NULL, Key_Read, & HKey))
This example also failed to process the possible movement of the color window, so I added the following sequence block after calling TestCooPERATIVELevel ().
/ / Make sure to operate in the screen control panel, monitor the movement of things
Rect RTMP;
GetWindowRect (hwnd, & rtmp);
g_pframework-> move (RTMP.LEFT, RTMP.TOP);
The Direct3D screen saver framework does give the start section of the ConfigureDialog function and a method for storing configuration data, but I have not expanded for MSDNSParkles because it is not a key detail.
Another "feature" of the sample screen saver is: it controls the color using WM_TIMER. On Windows 2000-based operating systems, this generates an appropriate frame rate, but Windows9X-based operating systems do not have such a WM_TIMER interval size that allows the use of a good frame rate. Considering this situation, I re-written this screen saver so that it is controlled using threads. I didn't include this part of the code, because it is a simple and clear thread implementation. Interested programmers can consider adding multi-monitor capabilities, power management, and passwords because the Direct3D screen saver frame does not provide these features.
High-level view
Since these details are not a problem, we can start. Figure 3 shows the contents of the Visual Studio Workspace window for the MSDNSParkles example. The project consists of screensaver.cpp files (which is obtained from the Direct 3D example, as described above), and sparkles.cpp.
Figure 3. MSDNSPARKLES project view
Sparkles.cpp contains two sets of functions. The first group is a function that can be covered, I will examine it later. The second group contains a flicker effect function. In order to process the flicker effect, MSDNSParkles defines its own small API, which is Randomtexture, RandomSparkle, INITSPARKLES, UPDATESPARKLE, and DRAWSPARKLE.
// Blinking effect function
INT randomtexture (int overflow);
Sparkle RandomSparkLe (Void);
Void Initsparkles (Void);
Void UpdateSparkles (Void);
Bool DrawSparkles (LPDirect3DDevice7 LPDEV, D3DVector from, D3DVector AT); All of these functions operate in addition to Randomtexture. The Sparkle structure includes information for the appearance and behavior of each particle, and each particle is part of the particle system.
// Structure of a blinking effect
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;
Elements Position and Velocity, using Scale and Delta_scale together by the particle system for generating various positions of particles. Element Texture and Color Definition as part of a particle appearance of a part of the agreed randomization algorithm.
Appearance is randomized by controlling the conversion by using an aging system, wherein the element AGE is given a countdown system. There are three ways to conversion:
When a textured time limit expires, randomly select a texture from the list. Color_Mode is used to generate a base color. Color_Modifier_Mode is used to modify the primary color in some interesting ways.
These random textures and colors will follow the appearance of the particles to be completely defined. The content generated by some of some of the finest random mathematics methods is indeed quite wonderful.
Randomtexture is used to help select a texture from the texture list.
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 starts by generating some color increment values. It then begins to place a sparkles structure, which represents particles with time limits, scale, and location values. The texture of the flicker effect is selected by calling Randomtexture. Subsequently, Color_Mode and Color_Modifier_Mode are used to generate the color of the particle.
COLOR_MODE determines which method is used to generate basic colors, random mode or RGB vibration mode. Random mode is just randomly selecting a color. RGB tremor mode makes some delicate color incremental calculations, moving smoothly in a certain color range. Color_Modifier_Mode controls the modification of the base color. You can choose four variables: saturated colors, gemstones, soft color or bright colors. Each of these modes perform a slightly different calculation to modify the gravity.
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.PSITION = 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: // Random
RET.COLOR = D3DVector (RND (), RND (), RND ());
Break;
Case 1: // RGB tremble
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: // No change
Break;
Case 1: // Saturation
Ret.color / = max (ret.color);
Break;
Case 2: // gem
Ret.color - = min (ret.color);
Ret.color / = max (ret.color);
Break;
Case 3: // Soft Color
Ret.color - = min (ret.color);
RET.COLOR / = max (ret.color); ret.color = d3dvector (0.6f) 0.4f * ret.color;
Break;
Case 4: // Bright colors and cold color, and can be used in most cases
RET.COLOR * = 1.2F;
Break;
DEFAULT:
Break;
}
Return Ret;
}
INITSPARKLES Selects the starting texture and then assigns memory for the particle list. A particle is substantially consisting of two triangles. For the description of the particles, see Figure 4.
Figure 4. Particle
Next, each flicker effect is randomly initialized through RandomSparkle. Finally, the subscript of the index drawing is generated for each particle.
Void INITSPARKLES (VOID)
{
TEXTURE = 1; // Start with DX7 bitmap
Sparkle = (sparkle *) malloc (nmaxnumsparkles * sizeof (sparkle));
For (uint i = 0; i Sparkle [I] = randomsparkle (); } // Set the subscript For (i = 0; 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 () ends UpdateSparkles decreases the current value of Texture_age, Color_age and Color_Modifier_age. Subsequently, if the time limit has been inverted to 0, a new random value of Texture (using randomtexture), color_mode, and color_modifier_mode will be generated. Once this operation is complete, RandomSparkle will be used again to randomly generate the position, texture, and color of each particle next frame. Finally adjust the scale. Void UpdatesParkles (Void) { UINT I; //0..n 0..n-1 == Current, n == random 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 == random, 1 == RGB tremble Color_age-- IF (color_age == 0) { Color_age = min_color_age (rD () * (max_color_age - min_color_age)); Color_mode = rand ()% Numcolormodes; } //0..5 0 == No change, 1 == saturated color, 2 == gem, 3 == soft color, // 4 == bright color, 5 == dark color, but not suitable for most 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; } / / Update the flashing effect For (i = 0; i Sparkle [i] .cur_age-- IF (Sparkle [i] .cur_age == 0) { Sparkle [I] = randomsparkle (); } Sparkle [i] .scale * = sparkle [i] .delta_scale; } } // updatesparkles () ends DrawSparkles calculates the front direction used in the particle system positioning code. This value is used to generate the offset of each particle vertex. Figure 5 illustrates this. Figure 5. Particle positioning In order to improve efficiency, the flicker effect is sorted according to the drawing texture. The index list and the DrawIndexedPrimitive are used to draw the quadrilateral shape of the particle system. 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); // Draw a flashing effect / / We want to increase the flicker effect of all the same texture // Batch processing and only one DrawPrim call INT FLAGS [NUMTEXTURES]; For (int tex = 0; Tex Flags [TEX] = 0; } / / Calculate which texture is being used For (i = 0; i Flags [sparkle [i] .texture] ; } / / For each texture, batch processing of flicker effects and draw For (Tex = 0; Tex IF (Flags [TEX] == 0) CONTINUE; / / Set the correct material / texture combination LPDEV-> SetTexture (0, g_ptexsparkletextures [tex]); // Construction quadrangular shape for batch INT NUM = 0; For (i = 0; 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 ; } // Completed the creation of batch processing, now coloring IF (LPDEV-> DrawIndexedprimitive (D3DPT_TRIANGLIST, D3DFVF_LVERTEX, (LPVOID) S_MESH, 4 * flags [tex], s_indices, 6 * flags [tex], 0)! = D3D_OK) Return False; } Return True; } // DrawSparkles () ends Figures 6 and 7 show an additional screen snapshot of an image generated by an Alpha mixing particle system. Figure 6. MSDNSPARKLES screen snapshot 2 Figure 7. MSDNSPARKLES screen snapshot 3 Internal component of MSDNSParkles Let us continue to explore the implementation process of overloading the application function. I reap out the prototype of the function below so you don't have to go back to the place where this article begins. I will examine each overload function that the Direct3D screen saver frame allows override. / / -------------------------------------------------------------------------------------------- ---------------------------- // External function prototype / / -------------------------------------------------------------------------------------------- ---------------------------- HRESULT APP_CONFIRMDEVICE (DDCAPS *, D3DDeviceDesc *); HRESULT APP_ONETIMESCENINIT (); Void app_deletedeviceObjects (hwnd, lpdirect3ddevice7); HRESULT APP_INITDEVICEOBJECTS (HWND, LPDIRECT3DDEVICE7); HRESULT APP_FRAMEMOVE (LPDIRECT3DDEVICE7, FLOAT); HRESULT APP_RENDER (LPDIRECT3DDEVICE7); HRESULT APP_RESTORESURFCESURFCES (); HRESULT APP_FINALCLANUP (); ConfirmDevice The MSDNSParkles app_confirmdevice function verifies whether the device can perform one: one alpha mixed. This is the simplest form of Alpha - and, according to my experience, all graphics cards can perform such alpha mixes. Below is the process of MSDNSParkles to implement App_ConfirmDevice. HRESULT APP_CONFIRMDEVICE (DDCAPS * PDDDRIVERCAPS, D3DDeviceDesc7 * pd3ddevicedesc) { / / Get Triangle Caps (hardware or software) and verify alpha mixing LPD3DPRIMCAPS PDPC = & PD3DDEVICEDESC-> DPCTRICAPS; IF (0 == (PDPC-> DWSRCBLENDCAPS & PDPC-> DWDESTBLENDCAPS & D3DBLEND_ONE)) Return E_FAIL; Return S_OK; } Onetimesceneinit The app_onetimesceneinit function first seizes the random number generator and sets the background color and the initialization texture list. It then creates the texture used by MSDNSParkles using the texture list. Finally, INITSPARKLES is called to initialize the particle system. HRESULT APP_ONETIMESCENEINIT () { / / Sowing the random number generator SRAND (Time (0)); // Initialization background color Bckcolor = D3DRGB (0,0,0); // Load texture data 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 D3DTextr_CreateTextureFromFile ((char *) g_szsparkletextures [i]); INITSPARKLES (); Return S_OK; } INitDeviceObjects The app_initdeviceObjects function uses the Helper function setTexturestate, SetRenderstate, and SetViewState. These functions are set respectively, and the coloring states and viewing systems used by the texture list, the application are set. Note that Alpha mixing is enabled, and the mixed color state will be set to ONE: One. Void SetTextureState (LPDIRECT3DDEVICE7 PD3DDEVICE) { / / Set texture status D3DTextr_RestoreAllTextures (PD3DDevice); // Load texture surface For (int i = 0; i g_ptexsparkletextures [i] = d3dtextr_getsurface ((char *) g_szsparkletextures [i]); } Void setRenderstate (LPDIRECT3DDEVICE7 PD3DDEVICE) { // alpha mixed color PD3DDEVICE-> Setrenderstate (D3Drenderstate_alphablendenable, true); PD3DDEVICE-> Setrenderstate (D3Drenderstate_srcblend, srcblend); PD3DDEVICE-> Setrenderstate (D3Drenderstate_Destblend, dstblend); // Filter status PD3DDEVICE-> SetTexturestageState (0, D3DTSS_MINFILTER, D3DFILTER_LINEAR); PD3DDEVICE-> SetTexturestageState (0, D3DTSS_Magfilter, D3DFILTER_LINEAR); PD3DDEVICE-> SetTextureStagestate (0, D3DTSS_MIPFILTER, D3DFILTER_LINEAR); / / Set non-textured coloring state PD3DDEVICE-> SetRenderstate (D3Drenderstate_dithereNable, False); PD3DDEVICE-> SetRenderstate (D3Drenderstate_SpeculaNable, False); PD3DDEVICE-> Setrenderstate (D3DrenderState_zwriteEnable, false); PD3DDEvice-> setrenderstate (D3Drenderstate_zenable, false); PD3DDEVICE-> Setrenderstate (d3drenderstate_cullmode, d3dcull_none); // Note: In DX7, you have to turn off the vertex lighting, you need to set D3DrenderState_Lighting to false / / (And switch to the color in D3DLVERTEX) PD3DDEVICE-> Setrenderstate (D3Drenderstate_Lighting, False); } Void SetViewState (LPDIRECT3DDEVICE7 PD3DDEVICE) { / / Get a high ratio PD3DDEVICE-> GetViewPort (& VP); FLOAT FASPECT = ((float) vp.dwheight) / vp.dwwidth; / / Setting the transformation matrix D3DUTIL_SETPROJECTIONMAMATRIX (Proj, g_pi / 4, // 1.0f; Faspect, 1.0F, max_dist); PD3DDEVICE-> SetTransform (D3DTransformState_Projection, & Proj); D3DUTIL_SETVIEWMAMATRIX (View, from, AT, UP); PD3DDEVICE-> SetTransform (D3DTransformState_View, & view); D3DUTIL_SETIDENTITYMAMATRIX (World); PD3DDEVICE-> SetTransform (D3DTransformState_World, & World); } HRESULT APP_INITDEVICEOBJECTS (HWND HWND, LPDIRECT3DDEVICE7 PD3DDEVICE) { // Check the parameters IF (null == pd3ddevice) Return E_INVALIDARG; / / Set texture status SetTexturestate (PD3DDEvice); / / Setting the coloring state SetRenderstate (PD3DDEvice); // Setting the view system SetViewState (PD3DDevice); Return S_OK; } The framemove app_framemove function updates the particle system simulation using UpdateSparkles. HRESULT APP_FRAMEMOVE (LPDIRECT3DDEVICE7 PD3DDEVICE, FLOAT FTIMEKEY) { // Good, now you can use the flashing effect. Updatesparkles (); Return S_OK; } Coloring App_Render functions is quite simple. First, we use time and RandomViewPoint to modify the viewpoint over time. Then we let the DrawSparkles draw the particle system. too easy! 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_SETVIEWMAMATRIX (View, from, AT, UP); PD3DDEVICE-> SetTransform (D3DTransformState_View, & view); } HRESULT APP_RENDER (LPDIRECT3DDEVICE7 PD3DDEVICE) { Static float tic = -rnd () * 10000.0F; / / Clear the view box PD3DDEVICE-> Clear (0ul, null, d3dclear_target, bckcolor, 1.0f, 0L); // Start the scene IF (Failed (PD3DDevice-> Beginscene ()))) Return S_OK; // Don't return A "Fatal" Error // tic back and forth moving items TiC = 0.005F; / / Use in random positioning 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; // Modify the view box RandomViewPoint (PD3DDEvice, TIC); // Draw a flashing effect IF (! DrawSparkles (PD3DDevice, from, AT)) Return E_FAIL; // End the scene PD3DDEVICE-> Endscene (); Return S_OK; } The deletedEviceObjects app_deleteDeviceObjects function fails to invalidate the texture in the texture list. Void App_DeletedEviceObjects (HWND HWND, LPDIRECT3DDEVICE7 PD3DDEVICE) { D3DTextr_INVALIDATETEXTURES (); } RESTORESURFACES { Return S_OK; } Finalcleanup app_finalcleanup function deletes the memory used by the flashing effect list. HRESULT APP_FINALCLANUP () { Free (sparkle); Return S_OK; }