|
Чтобы сделать правильный 2d бампмэппинг надо посчитать нормаль для
каждого пиксела в bumpmap (карте высот) и для вычисления цвета каждого
пиксела использовать угол между источником света и данной нормалью.
Для бампмэппинга в нашем случае используется источник света, бесконечно
близкий к освещаемой плоскости: координаты нормали nx и ny просто
разность высот между соседними пикселaми в bumpmap по осям X и Y.
Так как нормаль - просто вектор направления, и ее длина равна единице,
то nz=1-sqrt(nx2 + ny2).
1. Заранее вычисляем интенсивность света (light map) по вышеприведенной формуле. (Световое пятно с максимальной интенсивностью в центре). Полагаем как обычно, что интенсивность зависит от n_z. Пусть размер таблички будет для удобства 256x256.
2. Подбирая из 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.
|