2D Бампмэппинг ( Bumpmapping )
Материал из DRKB
Чтобы сделать правильный 2D бампмэппинг, надо посчитать нормаль для каждого пиксела в BumpMap (карте высот) и для вычисления цвета каждого пиксела использовать угол между источником света и данной нормалью.
Для бампмэппинга в нашем случае используется источник света, бесконечно близкий к освещаемой плоскости: координаты нормали nx и ny просто разность высот между соседними пикселaми в bumpmap по осям X и Y.
Так как нормаль - просто вектор направления, и ее длина равна единице, то nz=1-sqrt(nx2 + ny2).
- Заранее вычисляем интенсивность света (light map) по вышеприведенной формуле. (Световое пятно с максимальной интенсивностью в центре). Полагаем как обычно, что интенсивность зависит от n_z. Пусть размер таблички будет для удобства 256x256.
- Подбирая из lightmap интенсивность по формуле:
outvalue = lightmap[n_x+128][n_y+128]
(если использовать нормали в диапазоне -128...+127) мы получим корректную картину освещения для бесконечно близкого источника.
n_x = bumpmap[x+1][y] - bumpmap[x-1][y] n_y = bumpmap[x][y+1] - bumpmap[x][y-1]
Конечный цвет определяется так:
outvalue := lightmap[(n_x-(lightx-currentx))][(n_y-(lighty-currenty))].
Также нужно проверить, находимся ли мы внутри диапазона lightmap.
Вот пример:
program Bumpy_Surface;
{ Hичего не USES }
type
Tscr = array[0..199, 0..319] of Byte;
SegmentT = Array[0..65534] of Byte;
Virseg = ^SegmentT;
var
scr: Tscr absolute $A000:0000;
buf: Tscr;
ilx, ily: Integer;
i, j, k, nx, ny, nz, rx, ry, x, y, lx, ly, x1, y1: Integer;
tx, ty, tz: Real;
var
segm, offs: Integer;
Segment: Virseg;
litemap: Word;
procedure SetUpSegment(var Segname: Virseg; var add: word);
begin
GetMem(Segname, 65534);
add := seg(Segname^);
end;
procedure Wait; assembler;
asm
mov dx,3DAh;
@l1:
in al,dx;
and al,08h;
jnz @l1;
@l2:
in al,dx;
and al,08h;
jz @l2;
end;
procedure SetMode(Mode: Word);assembler;
asm
mov ax,Mode;
int 10h
end;
procedure FillBox(x, y, w, h: Integer; Color: Byte);
var
i, j, k: Integer;
begin
for i := y to y+h-1 do
for j := x to x+w-1 do
buf[i, j] := Color;
end;
procedure Print(x, y: Integer; s: string; xs, ys: Integer; Color: Byte);
var
i, j, k, c, px, py: Integer;
b: Byte;
begin
px := x;
py := y;
for k := 1 to Length(s) do
begin
c := Ord(s[k]);
for i := 0 to 7 do
begin
b := mem[segm:offs + c * 8 + i];
for j := 0 to 7 do
begin
if ((b shl j) and 128) <> 0 then
FillBox(x, y, xs, ys, Color);
x := x + xs;
end;
x := px;
y := y + ys;
end;
y := py;
px := px + xs * 8;
x := px;
end;
end;
procedure SetGradientPalette;
var
k, r, g, b: Byte;
begin
asm
mov dx,03c8h;
xor al,al;
out dx,al;
end;
r := 0;
g := 0;
for k := 0 to 255 do
begin
b := (k*63 div 255);
//r := b;
//g := b;
if k > 200 then
begin
r := r + 1;
g := g + 1;
end;
asm
mov dx,03c9h;
mov al,r;
out dx,al;
mov al,g;
out dx,al;
mov al,b;
out dx,al
end;
end;
end;
procedure Blur;
var
i, j, k: Integer;
begin
for i := 0 to 199 do
for j := 0 to 319 do
buf[i, j] := (buf[i-1, j] + buf[i+1, j] + buf[i, j-1] + buf[i, j+1]) div 4;
end;
procedure ASMBumpmapping; assembler;
asm
{ few times faster ;) }
mov ax, seg buf
mov es,ax
mov ax, offset buf
mov bx,320*3
add ax,bx
mov di,ax
mov si,bx
mov dx,199
sub dx,5
@1:
mov cx,320
@2:
xor bh,bh;
xor ah,ah;
mov al, es:[di+1]
mov bl, es:[di-1]
sub ax,bx
sub ax,320;
add ax,cx;
add ax,lx;
add ax,128;
cmp ax,255;
jc @ok1;
mov ax,255;
@ok1:
push ax
xor ah,ah;
mov al, es:[di+320]
mov bl, es:[di-320]
sub ax,bx
sub ax,197;
add ax,dx;
add ax,ly;
add ax,128;
cmp ax,255;
jc @ok2;
mov ax,255;
@ok2:
pop bx
mov bh,bl;
mov bl,al
push es
mov ax, litemap
mov es,ax
and bx,0FFFEh
mov bx,[es:bx]
mov ax, 0A000h
mov es,ax
mov [es:si],bx
pop es
inc di
inc si
loop @2
dec dx;
jnz @1
mov ax,$0a000
mov es,ax
xor ax,ax
mov [es:si-1],ax
end; { asm Bumpmaping }
procedure Bumpmapping;
begin
for y := 0+3 to 199-3 do
begin
for x := 0 to 319 do
begin
nx := buf[y+1, x] - buf[y-1, x];
ny := buf[y, x+1] - buf[y, x-1];
nx := nx - x + lx;
ny := ny - y + ly;
nx := nx + 128;
ny := ny + 128;
if (nx < 0) or (nx > 255) then nx := 0;
if (ny < 0) or (ny > 255) then ny := 0;
scr[y, x] := mem[litemap:nX+nY*256];
end;
end;
end;
begin
{ Достанем адрес знакогенератора }
asm
mov ax,$1130;
mov bh,03h;
int 10h;
mov segm,es;
mov offs,bp;
end;
{ установим режим и палитру }
SetMode($13);
SetGradientPalette;
{ Cгенерируем световое пятно }
SetUpSegment(Segment, litemap);
for y := 0 to 255 do
for x := 0 to 255 do
begin
tX := (x-128) / 128.0;
tY := (y-128) / 128.0;
tZ := 1-sqrt(tX*tX + tY*tY);
if (tZ < 0) then tZ := 0;
mem[litemap:x+y*256] := Round(tz * 254);
end;
{ Очистим буфер }
FillChar(buf, 64000, 0);
{ набросаем точек }
for i := 0 to 10000 do
FillBox(Random(320), Random(200), 1, 1, 255);
{ ...и размоем их }
Blur;
{ Hапишем текст }
Print(60, 65, 'BUMPY', 5, 5, 255);
Print(47, 115, 'SURFACE', 4, 4, 255);
{ ...и опять размоем }
Blur;
Blur;
Blur;
ilx := 5;
ily := 5;
ly := 100;
lx := 80;
repeat
//move(buf, scr, 64000);
wait;
//Bumpmapping;
ASMBumpmapping;
lx := lx + ilx;
if (lx > 320) or (lx < 0) then ilx := -ilx;
ly := ly + ily;
if (ly > 200) or (ly < 0) then ily := -ily;
until port[$60] = 1; { ESC }
{ сбросим буфер клавиатуры }
memw[$000:$041a] := memw[$000:$041c];
SetMode($3);
end.
Source: http://algolist.manual.ru
ID: 04121