Освещение по Фонгу

phong.jpg

Реализация метода освещение по Фонгу

Метод освещения по Фонгу сводит освещение к текстурированию по определенной текстуре.

Для данного метода необходима константа, точечный источник, удаленный бесконечно далеко.
Если в вершинах нормаль к объекту имеет длину 1, то при интерполяции этой нормали между вершинами по какой-то грани мы будем получать в каждой точке нормаль той же самой длины 1.

В общем случае интенсивность освещения находится по формуле:

intensity = ambient + amp * (N * L), где N - нормаль, L - непосредственный источник освещения

Когда вектор L = (0,0,1). Общий случай сводится к упрощенному:

intensity = ambient + amp * (N.x * L.x + N.y * L.y + N.z * L.z) =
= ambient + amp * (N.x * 0 + N.y * 0 + N.z * 1) =
= ambient + amp * N.z =
= ambient + amp * sqrt(1 - (N.x * N.x + N.y * N.y)).

То есть интенсивность выражается через N.x, N.y, а эти величины меняются линейно. N.x и N.y - числа с плавающей запятой от -1 до 1 (т.к. длина вектора равна 1), интерполировать их - занятие медленное, да корень считать раз в пиксел тоже не хочется. Поэтому вместо интерполяции N.x и N.y обычно интерполируют, например, 128*(N.x+1) и 128*(N.y+1), причем уже в целых числах. Тогда все возможные значения таким образом отмасштабировнных N.x, N.y - это 0, 1, ..., 255. Поэтому можно заранее посчитать табличку значений intensity для каждой пары отмасштабировнных N.x, N.y.

Линейно интерполируем 128*(N.x+1) и 128*(N.y+1) и по ним по таблице определяем интенсивность. Это и есть текстурирование, только в качестве текстуры используется таблица освещенности размером 256x256, а в качестве координат текстуры u, v для каждой вершины берутся отмасшатбированные координаты нормали в этой вершине.

uses crt;
const palwhite = 20; {Количество белых цветов}
var phongmap : pointer;
    x,y : integer;
    c,i : byte;
 
procedure setpal(c,r,g,b : byte);assembler;
asm mov dx,3C8h;mov al,c;out dx,al;inc dx;mov al,r;out dx,al;mov al,g
out dx,al;mov al,b;out dx,al;end;
 
procedure makepal(objr,objg,objb : byte);
begin
  {black to object color}
  for i := 0 to 127-palwhite do
    setpal(i,(objr*i) div (127-palwhite),(objg*i) div (127-palwhite),
      (objb*i) div (127-palwhite));
  {object color to white}
  for i := 0 to palwhite do
    setpal(i+127-palwhite,objr+((63-objr)*i) div palwhite,
      objg+((63-objg)*i) div palwhite,objb+((63-objb)*i) div palwhite);
end;
 
function length(x1,y1,x2,y2 : longint) : word;
begin
  length := round(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));
end;
 
begin
  getmem(phongmap,65535);
  for y := 0 to 127 do
  for x := 0 to 127 do begin
    c := length(x,y,128,128); {Задается длина}
    if c > 127 then c := 127;
    c := 127-c; {Реверс цвета, поэтому в центре н еполучается чёрный}
    mem[seg(phongmap^):y shl 8+x] := c;
    mem[seg(phongmap^):(255-y) shl 8+x] := c;
    mem[seg(phongmap^):y shl 8+(255-x)] := c;
    mem[seg(phongmap^):(255-y) shl 8+(255-x)] := c;
  end;
  asm
    mov ax,13h
    int 10h
  end;
  makepal(32,16,63); {Бирюзовый объект}
  {Прорисовка phongmap (в масштабе 1:2)}
  for y := 0 to 127 do
  for x := 0 to 127 do
    mem[$A000:x+y shl 8+y shl 6] := mem[seg(phongmap^):x shl 1+y shl 9];
  repeat until keypressed;
  asm
    mov ax,3h
    int 10h
  end;
end.
 
{вычисление нормалей}
const maxp = 900; {макс т.}
      maxpl = 900; 
var points : array[0..maxp,0..2] of integer; 
    planes : array[0..maxpl,0..2] of word;
    pnormals : array[0..maxpl,0..2] of integer; {Нормали плоскости}
    normals : array[0..maxp,0..2] of integer; {Угловые нормали}
    maxpoint,maxpoint : word; {Фактическое число точек}
    i,a,n : word;
    rx1,ry1,rz1,rx2,ry2,rz2 : integer;
    rx,ry,rz,l : real;
 
begin
  readobject; 
  for i := 0 to maxplane do begin
    rx1 := points[planes[i,1],0]-points[planes[i,0],0];
    ry1 := points[planes[i,1],1]-points[planes[i,0],1];
    rz1 := points[planes[i,1],2]-points[planes[i,0],2];
    rx2 := points[planes[i,2],0]-points[planes[i,0],0];
    ry2 := points[planes[i,2],1]-points[planes[i,0],1];
    rz2 := points[planes[i,2],2]-points[planes[i,0],2];
    {Вычисление плоских нормалей}
    pnormals[i,0] := ry1*rz2-ry2*rz1;pnormals[i,1] := rz1*rx2-rz2*rx1;
    pnormals[i,2] := rx1*ry2-rx2*ry1;
  end;
  for i := 0 to maxpoint do begin
    {Reset all variabels}
    rx := 0;ry := 0;rz := 0;n := 0;
    for a := 0 to maxplane do
      {Check if point is in plane}
      if (planes[a,0] = i) or (planes[a,1] = i) or (planes[a,2] = i) then
      begin
        {К нормали плоскости прибавляется нормаль угла и края}
        rx := rx+pnormals[a,0];ry := ry+pnormals[a,1];rz := rz+pnormals[a,2];
        {Increase numbers of planes}
        inc(n);
      end;
    if n > 0 then begin {n строго больше 0, иначе точка н еиспользуется}
      rx := rx/n;ry := ry/n;rz := rz/n; {Расчет средней нормали плоскости}
      l := sqrt(rx*rx+ry*ry+rz*rz); {Расчет длины нормали}
      if l = 0 then l := 1; {Для избежания деления на 0}
      {шкала нормалей}
      normals[i,0] := round(rx/l*120);
      normals[i,1] := round(ry/l*120);
      normals[i,2] := round(rz/l*120);
    end;
  end;
end.
 
 
begin
  { высчитывает phongmap, переходит в графический режим }
  repeat
    [Вращает точки и нормали, преобразовывает в 2d и сортирует]
    for i := 0 to maxplane do
        texture(
        {X и Y координаты для первого угла:}
        160+newp[planes[pind[i],0],0],100+newp[planes[pind[i],0],1],
        {U и V координаты для первого угла}
        128+newn[planes[pind[i],0],0],128+newn[planes[pind[i],0],1],
        {X и Y координаты для второго угла: и так далее}
        160+newp[planes[pind[i],1],0],100+newp[planes[pind[i],1],1],
        128+newn[planes[pind[i],1],0],128+newn[planes[pind[i],1],1],
        160+newp[planes[pind[i],2],0],100+newp[planes[pind[i],2],1],
        128+newn[planes[pind[i],2],0],128+newn[planes[pind[i],2],1]);
    {Восстанавливает в памяти, отображает картинку, очищает виртуальный экран}
  until keypressed;
end;

Ключевые слова: 
Освещение, интенсивность, диффузное, зеркальное, равномерное
ВложениеРазмер
PHONG1.rar7.17 кб