2D Бампмэппинг ( Bumpmapping )

Чтобы сделать правильный 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
Category:Алгоритмы
Category:Графика