I decided to fork this thread from this one:
http://nesdev.com/bbs/viewtopi ... 4&start=30
That's a very neat way to do it! I use the following:
you can ignore the 'lut' thing for now, that's just doing the converting to 00,Fx thing I was talking about before. Your way seems to involve much less shifting. I think I like your way better =)
The reason I convert 0,1,2,3 to 00,Fx is for the shorter attribute combination. The idea is the following:
- the easiest way to determine whether or not a pixel is transparent is to see if it's zero.
- if you OR attribute bits... transparent pixels will not be zero (since they will have their attribute bits set)
- you can get around that by conditionally ORing attribute bits (ie: only OR them if the original pixel is nonzero), but this requires an additional conditional for every onscreen sprite and BG pixel!
- by using 00,FD,FE,FF and combining attribute bits with AND, this ensures that pixel 0 will stay zero even after you combine attribute bits, but pixels 1-3 will retain their attribute bits.
A lot of this depends how you're rendering, too. But I think for the most part we all do it the same way, since at the end of the day you have to use the pixel in a palette lookup
Given BG pixel 'a' and sprite pixel 'b', you need to produce a value between 0x00-0x1F for the output (which goes to palette lookup). This value will be either the BG or sprite pixel depending on which has priority -- or it will be 0x00 if both are transparent. The simplest way to approach this that I have found is:
this is the shortest and simplest I've been able to make this code. Since it's run the most (256x240 times every frame) I figure this needs to be the quickest.
This only works if transparent pixels are always 0.. regardless of their attribute bits or other information (like the 0x80 sprite priority bit -- that must also be 0 if the pixel is transparent).
So then the next part of this trick is making transparent pixels always be 0 (rather than 0x04, 0x08, or 0x0C -- which are also transparent pixel values). This is where the AND trick comes in:
by combining attribute bits this way, BG_Pixel stays 0 for transparent pixels without any conditionals... since CHR_Pixel for pixel 0 is 0... anything you AND with it will also be 0.
Anyway that's my approach.
http://nesdev.com/bbs/viewtopi ... 4&start=30
Fx3 wrote:
Code:
unsigned char layerA = (src[8] & 0xAA) | ((*src >> 1) & 0x55);
unsigned char layerB = ((src[8] & 0x55) << 1) | (*src & 0x55);
unsigned char layerB = ((src[8] & 0x55) << 1) | (*src & 0x55);
That's a very neat way to do it! I use the following:
Code:
static const u8 lut[4] = {0x00,0xFD,0xFE,0xFF};
u8 a = src[0];
u8 b = src[8];
dst[0] = lut[ ((a >> 7) & 1) | ((b >> 6) & 2) ];
dst[1] = lut[ ((a >> 6) & 1) | ((b >> 5) & 2) ];
dst[2] = lut[ ((a >> 5) & 1) | ((b >> 4) & 2) ];
dst[3] = lut[ ((a >> 4) & 1) | ((b >> 3) & 2) ];
dst[4] = lut[ ((a >> 3) & 1) | ((b >> 2) & 2) ];
dst[5] = lut[ ((a >> 2) & 1) | ((b >> 1) & 2) ];
dst[6] = lut[ ((a >> 1) & 1) | ((b ) & 2) ];
dst[7] = lut[ ((a ) & 1) | ((b << 1) & 2) ];
u8 a = src[0];
u8 b = src[8];
dst[0] = lut[ ((a >> 7) & 1) | ((b >> 6) & 2) ];
dst[1] = lut[ ((a >> 6) & 1) | ((b >> 5) & 2) ];
dst[2] = lut[ ((a >> 5) & 1) | ((b >> 4) & 2) ];
dst[3] = lut[ ((a >> 4) & 1) | ((b >> 3) & 2) ];
dst[4] = lut[ ((a >> 3) & 1) | ((b >> 2) & 2) ];
dst[5] = lut[ ((a >> 2) & 1) | ((b >> 1) & 2) ];
dst[6] = lut[ ((a >> 1) & 1) | ((b ) & 2) ];
dst[7] = lut[ ((a ) & 1) | ((b << 1) & 2) ];
you can ignore the 'lut' thing for now, that's just doing the converting to 00,Fx thing I was talking about before. Your way seems to involve much less shifting. I think I like your way better =)
Quote:
So, values are not 00, Fx as you mentioned.
The reason I convert 0,1,2,3 to 00,Fx is for the shorter attribute combination. The idea is the following:
- the easiest way to determine whether or not a pixel is transparent is to see if it's zero.
- if you OR attribute bits... transparent pixels will not be zero (since they will have their attribute bits set)
- you can get around that by conditionally ORing attribute bits (ie: only OR them if the original pixel is nonzero), but this requires an additional conditional for every onscreen sprite and BG pixel!
- by using 00,FD,FE,FF and combining attribute bits with AND, this ensures that pixel 0 will stay zero even after you combine attribute bits, but pixels 1-3 will retain their attribute bits.
A lot of this depends how you're rendering, too. But I think for the most part we all do it the same way, since at the end of the day you have to use the pixel in a palette lookup
Given BG pixel 'a' and sprite pixel 'b', you need to produce a value between 0x00-0x1F for the output (which goes to palette lookup). This value will be either the BG or sprite pixel depending on which has priority -- or it will be 0x00 if both are transparent. The simplest way to approach this that I have found is:
Code:
a = BG_Pixel;
b = Sprite_Pixel; // will be ORd with 0x80 if it has foreground priority
// apply clipping here -- you can set 'a' or 'b' to 0 if sprites/bg is disabled
// or if this is being clipped from the left-8 pixels or whatever
if(dot < ppu.nBGClip) a = 0;
if(dot < ppu.nSpClip) b = 0;
// determine whether to output BG or sprite pixel
// you output 'a' (the BG pixel) unless:
// a is zero (transparent)
// or sprite pixel 'b' has foreground priority
if(!a || (b & 0x80) )
a = b & 0x1F;
// if both a and b were zero (both pixels transparent)
// result with be 0x00 here, which will output $3F00
// 'a' is now 0x00-0x1F -- our output pixel
OutputPixel(a);
b = Sprite_Pixel; // will be ORd with 0x80 if it has foreground priority
// apply clipping here -- you can set 'a' or 'b' to 0 if sprites/bg is disabled
// or if this is being clipped from the left-8 pixels or whatever
if(dot < ppu.nBGClip) a = 0;
if(dot < ppu.nSpClip) b = 0;
// determine whether to output BG or sprite pixel
// you output 'a' (the BG pixel) unless:
// a is zero (transparent)
// or sprite pixel 'b' has foreground priority
if(!a || (b & 0x80) )
a = b & 0x1F;
// if both a and b were zero (both pixels transparent)
// result with be 0x00 here, which will output $3F00
// 'a' is now 0x00-0x1F -- our output pixel
OutputPixel(a);
this is the shortest and simplest I've been able to make this code. Since it's run the most (256x240 times every frame) I figure this needs to be the quickest.
This only works if transparent pixels are always 0.. regardless of their attribute bits or other information (like the 0x80 sprite priority bit -- that must also be 0 if the pixel is transparent).
So then the next part of this trick is making transparent pixels always be 0 (rather than 0x04, 0x08, or 0x0C -- which are also transparent pixel values). This is where the AND trick comes in:
Code:
static const u8 at_lookup[4] = { 0x03,0x07,0x0B,0x0F };
u8 at = attribute_bits; // I'll spare you my calculations here, but basically
// the 'at' gets a value 0-3 for the attribute bits
at = at_lookup[ at ]; // left shift by 2, OR with 3
BG_Pixel = CHR_Pixel & at; // combine attribute bits with CHR bits
u8 at = attribute_bits; // I'll spare you my calculations here, but basically
// the 'at' gets a value 0-3 for the attribute bits
at = at_lookup[ at ]; // left shift by 2, OR with 3
BG_Pixel = CHR_Pixel & at; // combine attribute bits with CHR bits
by combining attribute bits this way, BG_Pixel stays 0 for transparent pixels without any conditionals... since CHR_Pixel for pixel 0 is 0... anything you AND with it will also be 0.
Anyway that's my approach.