| Autor: | Wojciech Muła |
|---|---|
| Dodany: | 31.07.2002 |
| Aktualizacja: | 9.08.2002 |
Każdy z wierzchołków charakteryzują następujące parametry:
Zanim trójkąt zostanie wyświetlony jest on dzielony na dwie części, w taki sposób jak na obok (odcinek pomiędzy V2 i V2' jest najdłuższym scanlinem w całym trójkącie). Wypełnienie trójkątów V1, V2, V2' oraz V2, V2', V3 jest bardzo proste.
Obliczenie współrzędnych xL i xR oraz kolorów cL, cR wymaga wyłącznie dodawania przyrostów dXdY i dcdY (dXdY = dX/dY, dcdY = dc/dY).
xL = ydXdYL xR = ydXdYR
cL = ydcdYL cR = ydcdYR
Dla każdej linii należy policzyć przyrost dcdX.
dcdX = (cR − cL)/(xR − xL)
Po uproszczeniu powyższego równania zmienna y zostanie wyrugowana — współczynnik dcdX jest stały dla całego trójkąta (dla obu połówek!).
float dcdX;
void fill_scanline(int xl, int xr, int y, float cl) {
while (xl <= xr) {
putpixel(xl++, y, int(cl));
cl += dcdX;
}
}
Ponieważ wartości składowych kolorów są z przedziału 0..255, oraz w każdy niezdegnerowanym trójkącie V2'.x − V2.x > 0 to |dcdX| ≤ 255. Można zatem wartość dcdX zapisać w formacie fixed-point 8:8 i przy użyciu rozkazów MMX obliczać jednocześnie kolory czterech sąsiednich pikseli. Poniżej fragment kodu dla obrazów grayscale.
segment .text
; edi - adres xl
; ecx - szerokość scanlina w pixelach
; mm6 = | cl*4 | cl*3 | cl*2 | cl*1 | -- liczby fixed-point 8:8
; mm7 = |dcdX*4|dcdX*3|dcdX*2|dcdX*1| -- liczby fixed-point 8:8
fill_scanline_mmx:
push ecx
movq mm0, mm6 ; kopia robocza kolorów
shr ecx, 8 ; liczba 8-bajtowych bloków
jz .skip1
.loop1:
movq mm1, mm0
psrlw mm1, 8 ; podziel przez 8
packuswb mm1, mm1 ; spakuj
movd [edi], mm1
add edi , byte 8
paddw mm0, mm7 ; uaktualnij kolory
loop .loop1
.skip1:
pop ecx
and ecx, 0x7
jz .skip2
movq mm1, [ecx-8+mask] ; załaduj maskę
maskmovq mm0, mm1 ; (mm1[i] & 0x80) ? [ds:edi] = mm0[i], i=0..7
.skip2:
ret
segment .data
mask:
db 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 1
db 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 2
db 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 ; 3
db 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 ; 4
db 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 ; 5
db 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 ; 6
db 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 ; 7
Wartość rejestru mm7 jest ustalana na początku procedury wypełniającej trójkąt, z kolei zawartość rejestru mm6 jest obliczana w każdej pętli:
// wartości ustalane na początku pętli:
// mm5 = |4*dcdY_{L}|4*dcdY_{L}|4*dcdY_{L}|4*dcdY_{L}|
// mm6 = | 4*c_{L} | 3*c_{L} | 2*c_{L} | 1*c_{L} |
for (y=V1.y; y < V2.y; y++)
{
asm {
; parametry
call fill_scanline_mmx
paddw mm6, mm5
}
}
Przy dużych wartościach współczynnika dcdX jego wartość pomnożona przez 2, 3 i 4 nie mieści się w zakresie fixed-point 8:8. Jednakże tak duże wartości występują przy wąskich trójkątach, tj. gdy V'2X − V2X < 4, oraz gdy abs(V'2C − V2C) = 255. Wtedy pola na których występuje przekorczenie zakresu i tak nie są wyświetlane.