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

Материал из DRKB



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