Saturday, September 09, 2006

Alpha blending.

Okay, i'm trying to make sense of the alpha-blending bits of the nintendo DS. I'd like a blended grid for my editor ;)

So, let's see the specs first:

4000050h 2 R/W BLDCNT Color Special Effects Selection (BLEND_CR)
4000052h 2 W BLDALPHA Alpha Blending Coefficients (BLEND_AB)
4000054h 2 W BLDY Brightness (Fade-In/Out) Coefficient (BLEND_Y)
Bion. Voyons voir comment marche l'alpha-blending -- c'est à dire la transparence sur les fonds -- sur la console DS (j'aimerais bien pouvoir "estomper" la grille de mon sprite editor, si vous voulez tout savoir). Trois registres jouent un rôle dans les "effets spéciaux" (fade-in/fade-out et blending) : BLEND_CR, BLEND_AB et BLEND_Y. Je suis reparti de la démo Complex2D du devkitpro pour étudier comment ça marche.

You have 3 registers responsible of "special effects", including fade-in, fade-out and alpha blending. The latter one is the one we want (mixing two layers to have one of them appearing "translucent" and seeing the second one through. Clearly, we want it for fogs, clouds, mirrors, ghosts and plenty other reasons ;)

So, let's take the Complex2D demo from devkitpro and spice it up a little bit.

//set up the sub display
videoSetModeSub(MODE_0_2D |
DISPLAY_SPR_1D_LAYOUT |
DISPLAY_SPR_ACTIVE |
DISPLAY_BG0_ACTIVE |
DISPLAY_BG1_ACTIVE );

//set up two backgrounds to scroll around
SUB_BG0_CR = BG_COLOR_256 | (1 << SCREEN_SHIFT) | BG_PRIORITY(0);
SUB_BG1_CR = BG_COLOR_256 | (2 << SCREEN_SHIFT) | BG_PRIORITY(1);
BG_PALETTE_SUB[0] = RGB15(10,10,10);
BG_PALETTE_SUB[1] = RGB15(0,16,0);
BG_PALETTE_SUB[2] = RGB15(0,0,31);
vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);

There is two background layers, one being green-checker (BG0) and the other one being blue-checker (BG1). Each checker's "dark" squares are actually see-through pixels (purely transparent), which you achieve with color 0. Moreover, you have the 'backdrop' layer (here a dark-grey colour) that is seen when all pixels of all layers use color 0 (you typically never see the backdrop in a game, for instance).

Dans cette démo, on construit deux damiers, l'un vert (BG0) et l'autre bleu (BG1). Les cases "noires" de ces damiers sont en fait des pixels complètement transparents (couleur #0) et la couleur de fond (le "backdrop" -- que l'on préfère ne jamais voir dans un jeu vidéo) est gris-foncé.

//load the maps with alternating tiles (0,1 for bg0 and 0,2 for bg1)
for(iy = 0; iy < 32; iy++)
for(ix = 0; ix <32; ix++) {
map0[iy * 32 + ix] = ((ix ^ iy) & 2)>>1;
map1[iy * 32 + ix] = ((ix ^ iy) & 2);
}

//fill 2 tiles with different colors
for(i = 0; i < 64 / 2; i++) {
BG_GFX_SUB[i+32] = 0x0101;
BG_GFX_SUB[i+32+32] = 0x0202;
}


So far, we'll use SUB_BLEND_AB=0x1010, which is full intensity for both layers. If one is blue, the other red and that blending occurs, we should see purple somewhere. The initial priority is set to green-over-blue and we slowly scroll the green checker over the blue one. No blending is activated so far.

  • with SUB_BLEND_CR=BLEND_ALPHA|BLEND_SRC_BG0|BLEND_SRC_BG1; we do not change much things, as both layers are used as "blending source" but no layer is definde "transparent".

  • SUB_BLEND_CR=BLEND_ALPHA|BLEND_SRC_BG0|BLEND_DST_BG1; is more interesting. The green checker is still over the blue one, but it no longer "hides" blue pixels. Instead, it mixes with them to produce
    light-blue.

  • SUB_BLEND_CR=BLEND_ALPHA|BLEND_DST_BG0|BLEND_SRC_BG1; and SUB_BLEND_CR=BLEND_ALPHA| BLEND_DST_BG0|BLEND_DST_BG1|BLEND_SRC_BACKDROP appear such as the initial (no destination) blending.


Now, if we swap the priorities (e.g. blue checker-BG1 over green checker-BG0), the first setting (SRC_BG0|SRC_BG1) still show both layers opaque, but now that's (DST_BG0|SRC_BG1) that shows
alpha-blending while (SRC_BG0|DST_BG1) only shows opaque pixels.

In other words:
  • the source layer(s) must be over destination layer(s) to have some blending displayed. that means the priority of source layers should be numerically lower than the priority of destination layers.

  • the source layer is what is going to be translucent, the destination(s) is what is going to be seen through (and yes, i find this rather confusing).

En clair, le layer 'source' doit être par-dessus le layer "destination" pour que l'effet soit visible. Cela signifie que la priorité du layer "source" doit être numériquement inférieure à celle du layer "destination". Et oui, la libnds appelle "source" le layer qui sera transparent et "destination" le layer que l'on voit à travers l'effet. C'est perturbant, mais c'est comme ça.
J'ai parlé de transparence 50/50, mais une valeur 0x1010 pour BLEND_AB correspond plutôt à une composition additionnelle: R = 1*Ra + 1*Rb, V = 1*Va + 1*Vb ... ce qui peut être perturbant aussi. Supposons qu'on veuille faire un nuage ou un fantôme (qui sera donc notre source), on prendra une valeur de 1 pour le multiplicateur des layers-destination (histoire que le fantôme n'assombrisse jamais le décor) mais le fantôme étant du blanc pur, on ne l'affichera qu'à 50%, ce qui donnera BLEND_AB = 0x0810.

Note that calling 0x1010 a 50/50 blending is a bit exagerated. what it means is that you end up with a pixel whose values are R = 1*Ra + 1*Rb, G = 1*Ga +1*Gb and B = 1*Ba + 1*Bb. That may be a bit confusing too.
For clouds/ghosts, you want the cloud-carrying layer to be the source. The other layers should keeps they alpha-blending value to 1 (so that the cloud doesn't "darken" them), but the cloud itself (bright white) should e.g. only affect by 0.5, so that your grey tower appearslightgrey where the cloud is. That leads to e.g. BLEND_AB=0x0810.

The full demo code is available here.
Btw, See also liranuna's witch demo for alpha-blending of sprites over backgrounds.

No comments: