Чтобы сделать правильный 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 {Bumpmapping} 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;{ Bumpmapping } 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.
8 8 8
|