Помощь - Поиск - Пользователи - Календарь
Полная версия: Закраска по методу Гуро и Фонга
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи
18192123
Мне нужно изобразить тетраэдр , выполнить закраску Фонга относительно выбранного источника света, разработать процедуру управления скоростью вращения тела с одинаковыми ускорениями по осям и обеспечения перехода на низкозатратную процедуру Гуро и далее на закраску с использованием таблиц освещенности при увеличении угловых скоростей вращения.

Вот... Не знаю даже, с чего начать... Теорию по методам закраски изучила.... но с таким никогда не сталкивалась, поэтому с реализацией большие проблемы....

вот на что думаю опираться:
1. Получить нормаль грани: выбрать два вектора, лежащие в этой грани и найти их векторное произведение, нормировать этот вектор.
2. Записать найденные нормали граней в массив (normg[i]). Чтобы определить нормаль каждой вершины, определяем из таблицы граней для каждой вершины, какие грани в ней сходятся, и для этих найденных граней их нормали суммируем, а сумму нор-мируем (массив normv[i]).
3. Алгоритм Гуро. После того, как нормали вершин найдены, вы-числяем согласно модели освещения освещенность каждой вершины и записываем освещенности в массив освещенностей. Этот массив будет использоваться в процедуре рисования грани.
Алгоритм Фонга. Координаты найденных нормалей вершин пе-реводятся в сферические координаты, так что нормали представле-ны двумя числами типа byte. Нормали в таком виде будут использо-ваны процедурой рисования грани.
4. Реализовать процедуру рисования грани, для чего необходимо провести интерполяцию для определения освещенности (в ме-тоде Гуро) и нормали (в методе Фонга) для каждой точки гра-ни.
Закраску граней можно произвести путем заполнения внутренней области этой грани отрезками прямых, параллельных оси OX. Для этого найдем вершины грани с наименьшей и наибольшей координатой Y.
В качестве начальной возьмем вершину с наименьшей координатой Y. Разобьем условно множество ребер, составляющих грань, на две группы – левую и правую. Будем по очереди брать одно ребро из левой и одно из правой части, т.е. всегда работать с парой ребер. Выбор ребер может производиться следующим образом: сначала берем в качестве левого ребра ребро, начинающееся в начальной вершине и заканчиваю-щееся в следующей по порядку при обходе влево. Это ребро (l0,lk). Аналогично, в качестве первого правого ребра – ребро (r0,rk). Теперь в цикле будем строить прямые, параллельные оси X, начиная с прямой Y = Ymin, каждый раз увеличивая ординату. Для каждой этой прямой бу-дем находить точки пересечения с нашей парой боковых ребер. Если одно из боковых ребер не имеет точки пересечения с этой прямой, то его отбрасываем и берем следующее из его группы (левой или правой).
Если точки пересечения найдены, то найдем, какая часть левого и правого ребра пройдена, это значение запишется соответственно в пе-ременные tl и tr. Значение переменных tl и tr используется на первом шаге билинейной интерполяции в методе Гуро для нахождения осве-щенности в точках пересечения прямой, параллельной оси Х, и ребер, а в методе Фонга для определения нормалей тела в этих точках. Второй шаг интерполяции ведется аналогично, только вдоль отрезка, заключен-ного между парой ребер. Таким образом, вычисляется необходимая ин-формация для каждой точки грани.

но как всё это реализовать - не знаю! С чего начинать! Может быть кто-нибудь может привести реализацию методов закраски?
18192123
получилось нарисовать фигуру и вращать её, вот только с соединениями линий - путаница.... где ошибка?

uses Graph,crt;
const tetr: array[0..11] of real =
       (-1,-1,-1,-1,-1,1,-1,1,-1,
        -1,1,1);
     line_: array[0..11] of integer = (0,1,0,4,0,2,1,3,1,5,2,3);
       var    xt,yt,zt:real;
    x,y,z:real;
    sx,sy,sx1,sy1,p,zoom: integer;

procedure draw(color:byte);
begin
 for p:=0 to 5 do begin
  sx:=round(zoom*tetr[line_[p*2]*3])+260;
  sy:=round(zoom*tetr[line_[p*2]*3+1])+300;
    sx1:=round(zoom*tetr[line_[p*2+1]*3])+260;
  sy1:=round(zoom*tetr[line_[p*2+1]*3+1])+300;
  setcolor(color);
  line(SX,SY,sx1,sy1);
 end;
end;

procedure calc;
begin
 for p:=0 to 3 do begin
  Yt := tetr[p*3+1] * COS(X) - tetr[p*3+2] * SIN(X);
  Zt := tetr[p*3+1] * SIN(X) + tetr[p*3+2] * COS(X);
  tetr[p*3+1] := Yt;
  tetr[p*3+2] := Zt;

  Xt := tetr[p*3] * COS(Y) - tetr[p*3+2] * SIN(Y);
  Zt := tetr[p*3] * SIN(Y) + tetr[p*3+2] * COS(Y);
  tetr[p*3] := Xt;
  tetr[p*3+2] := Zt;

  Xt := tetr[p*3] * COS(Z) - tetr[p*3+1] * SIN(Z);
  Yt := tetr[p*3] * SIN(Z) + tetr[p*3+1] * COS(Z);
  tetr[p*3] := Xt;
  tetr[p*3+1] := Yt;
 end;
end;


var
 gd,gm:integer;
 t:char;
begin
 gd:=detect; initgraph(gd,gm,'');
 Z :=  0.1;
 Y :=  0.1;
 X :=  0.1;
 zoom:=70;

 repeat
  draw(15);
  delay(20000);
  draw(0);
  calc;
  if keypressed then begin
   t:=readkey;
   case t of
    '=':zoom:=zoom+1; {+}
    '-': zoom:=zoom-1;{-}
   end;
  end
 until t=#13;;
 closegraph;
end.



18192123
а как выбрать источник света и как с ним работать?
volvo
Марина, загляни вот сюда:
Dasaev Demo Guide v1.1

Там у него описывается работа с закрасками и по Гуро и по Фонгу...
18192123
Цитата(volvo @ 18.04.2007 0:07) *

Марина, загляни вот сюда:
Dasaev Demo Guide v1.1

Там у него описывается работа с закрасками и по Гуро и по Фонгу...

зашла по ссылке, скачала архив, но там полная ерунда - нужные файлы, где должны быть исходники, содержат только набор разных символов!
18192123
а может есть ещё что-то такое?
18192123
Нашла вот такую программу для закраски тетраэдра:


program lab5;
uses graph,crt;
const n=8;
procedure draw;
label
 nextj;
 type
         tetr=array[1..n] of record x,y,z:integer end;
         const
    verts :  tetr=(
   (x:-1; y:1; z:1),
    (x:1; y:1; z:1),
     (x:-1; y:1; z:-1),
      (x:-1; y:-1; z:1),
      (x:1;y:-1;z:-1),
      (x:1;y:-1;z:1),
      (x:1;y:1;z:-1),
      (x:-1;y:-1;z:-1) );
       g:array[1..n,1..4] of shortint=
       (
       (1,4,2,3),
       (1,3,2,4),
       (3,4,2,1),
       (4,1,3,2),
       (2,4,1,3),
       (3,2,4,1),
       (2,4,3,1),
       (4,1,2,3));
   var
   alfa,beta,gamma,
   teta,
   dt:real;
   c:tetr;
   procedure rotate(var x,y,z:integer);
   var
   cost,sint,one_cost,aone_cost,bone_cost,gsint:real;
   xn,yn,zn:integer;
   begin
   cost:=cos(teta);
   sint:=sin(teta);
   one_cost:=1.0-cost;
   aone_cost:=alfa*one_cost;
   bone_cost:=beta*one_cost;
   gsint:=gamma*sint;

   xn:=trunc(
   x*(cost +alfa*aone_cost)+
   y*(gsint +beta *aone_cost)+
   z*(-beta*sint +gamma*aone_cost));
 yn:=trunc(
 x*(-gsint+ beta*aone_cost)+
 y*(cost +beta*bone_cost)+
 z*(alfa*sint + gamma*bone_cost));
 zn:=trunc(
 x*(beta*sint  + gamma*aone_cost)+
   y*(-alfa*sint +gamma *bone_cost)+
   z*(-beta*sint +gamma*one_cost));
   x:=xn;y:=yn;z:=zn;
   end;
   function minz:integer;
   var j,m:integer;
   begin
   m:=1;
   for j:=1 to n do
   if c[j].z<c[m].z then m:=j;
   minz:=m;
   end;
   var
   pnts:array[1..n] of record
   x,y:integer end ;
   min:integer;
   x0,y0:integer;
   a:integer;
   i,j,k:integer;
   page:word;
   begin
   page:=0;
   x0:=300;
   y0:=150;
   a:=70;
   alfa:=0.6;
   beta:=0.7;
   gamma:=sqrt(1.0-alfa*alfa-beta*beta);
   teta:=0;
   dt:=2*pi/100; for i:=1 to n do
   begin
   c[i].x:=verts[i].x*a;
   c[i].y:=verts[i].y*a;
   c[i].z:=verts[i].y*a;
   rotate(c[i].x,c[i].y,c[i].z)
   end;
   min:=minz;
   setvisualpage((page+1)mod 2);
   for k:=0 to 2500 do
   begin
   setactivepage(page);
   for j:=1 to 8 do
   begin
   for i:=1 to 5 do if min=g[j,i] then goto nextj;
   for i:=1 to 5 do
   begin
   pnts[i].x:=x0+c[g[j,i]].x;
    pnts[i].y:=y0+trunc(0.775*c[g[j,i]].y)
    end;
    setfillstyle(solidfill,word(j+4));
    fillpoly(3,pnts) ; delay(100);
                    nextj:
                    end;
                    setvisualpage(page);
                    setactivepage((page+1)mod 2);
                    for i:=1 to n do
                    begin
                    c[i].x:=verts[i].x*a;
                    c[i].y:=verts[i].y*a;
                    c[i].z:=verts[i].z*a;
                    rotate(c[i].x,c[i].y,c[i].z)
                    end;
                    min:=minz;cleardevice;
                    teta:=teta+dt;
                    page:=(page+1)mod 2;
                    if keypressed then
                    exit;
                    end;
                    end;
              var drv,mode:integer;
              begin
              drv:=ega;
              mode:=egahi;
              initgraph(drv,mode,'');
              draw;
              closegraph;
              end.




мне здесь не понятно назначение функции minz, а также что хранится в этом массиве записей и для чего это нужно:

pnts:array[1..n] of record
   x,y:integer end ;



а самое главное - не понятна главная часть процедуры Рисование(procedure draw;), каким образом там производится рисование и закраска? пожалуйста, объясните основные моменты этого куска!
18192123
Если тетраэдр с его вершинами и сторонами задаётся таким образом (см. код), то как выбрать два вектора, лежащие в какой-то грани и так для всех граней?

tetr=array[1..n] of record x,y,z:integer end;
         const
    verts :  tetr=(
   (x:-1; y:1; z:1),
    (x:1; y:1; z:1),
     (x:-1; y:1; z:-1),
      (x:-1; y:-1; z:1),
      (x:1;y:-1;z:-1),
      (x:1;y:-1;z:1),
      (x:1;y:1;z:-1),
      (x:-1;y:-1;z:-1) );
       g:array[1..n,1..4] of shortint=
       (
       (1,4,2,3),
       (1,3,2,4),
       (3,4,2,1),
       (4,1,3,2),
       (2,4,1,3),
       (3,2,4,1),
       (2,4,3,1),
       (4,1,2,3));


18192123
и что значит "нормировать" вектор? ( как это производится?)
Malice
Вот еще с далеких фидошных времен откопался примерчик заливки по фонгу, думаю поможет: Нажмите для просмотра прикрепленного файла
18192123
Цитата(Malice @ 19.04.2007 22:55) *

Вот еще с далеких фидошных времен откопался примерчик заливки по фонгу, думаю поможет: Нажмите для просмотра прикрепленного файла

Спасибо!
Но лучше уж я с чистого листа начну....
Вопросы из сообщений 8 и 9 до сих пор для меня не ясны...
18192123
Цитата(18192123 @ 20.04.2007 0:26) *

Спасибо!
Но лучше уж я с чистого листа начну....
Вопросы из сообщений 8 и 9 до сих пор для меня не ясны...

Почему никто не хочет мне этого объяснить на моём примере?
Я хочу сделать сама, но мне нужно вначале понять, как начинать, а начинать нужно с взятия нормалей, нахождения их векторного произведения и нормирования полученного вектора....
Ну не пойму я, как выбрать в начале вектора!
( а в программе, которую оставил Malice для меня всё не понятно! Да и объёмчик внушительный!)
Lapp
Цитата(18192123 @ 19.04.2007 22:49) *

и что значит "нормировать" вектор? ( как это производится?)

Я думаю, нормировать - это значит сделать так, чтобы его длина была равна 1, сохранив направление прежним.


Добавлено через 2 мин.
Цитата(18192123 @ 19.04.2007 19:19) *

задаётся таким образом (см. код),

Мне непонятен способ задания тетраедра. Это вершины? или что?
Поясни, пожалуйста.
18192123
Цитата(Lapp @ 20.04.2007 9:18) *

Я думаю, нормировать - это значит сделать так, чтобы его длина была равна 1, сохранив направление прежним.

blink.gif никогда о таком не слышала...а можно показать на примере, как это сделать?

Добавлено через 7 мин.
Цитата(Lapp @ 20.04.2007 9:18) *


Мне непонятен способ задания тетраедра. Это вершины? или что?
Поясни, пожалуйста.

х, у, z - это вершины
g:array[1..n,1..4] - линии

но мне кажется ,что лучше задавать эти величины так ,как я пыталась выше (не совсем удачно..)

const tetr: array[0..11] of real =
       (-1,-1,-1,-1,-1,1,-1,1,-1,
        -1,1,1);
         line_: array[0..11] of integer = (0,1,0,4,0,2,1,3,1,5,2,3);



у меня тут линий не хватает вроде...
Lapp
Цитата(18192123 @ 20.04.2007 9:21) *

blink.gif никогда о таком не слышала...а можно показать на примере, как это сделать?

Ai'=Ai/|A|

Остальное позже. Машина в ремонте, автобус уходит.. smile.gif
18192123
Цитата(Lapp @ 20.04.2007 9:37) *

Ai'=Ai/|A|

Остальное позже. Машина в ремонте, автобус уходит.. smile.gif


|A| - поясни пожалуйста, это модуль чего? (без i)
Lapp
Цитата(18192123 @ 20.04.2007 9:48) *

|A| - поясни пожалуйста, это модуль чего? (без i)

Модуль вектора А.
Malice
Цитата(18192123 @ 19.04.2007 13:10) *

мне здесь не понятно назначение функции minz

поиск минимальной Z-координаты

Цитата(18192123 @ 19.04.2007 13:10) *

а также что хранится в этом массиве записей и для чего это нужно:

pnts:array[1..n] of record
   x,y:integer end ;


а самое главное - не понятна главная часть процедуры Рисование(procedure draw;), каким образом там производится рисование и закраска? пожалуйста, объясните основные моменты этого куска!

pnts- массив всех вершин грани, используется для функции FillPoly, которая выполняет заливку.

Цитата
икогда о таком не слышала...а можно показать на примере, как это сделать?

находишь длину вектора нормали и дельшь все его координаты на длину. Получается вектор длиной =1.

Цитата
у меня тут линий не хватает вроде...

Как бы да smile.gif У тебя всего 4 точки (0..3), а в описаниях граней присутствуют цифры 4 и 5 smile.gif
Вот так попробуй чтоли:
array[0..11] of real =   (1,0,0, 0,1,0, 0,0,1, -0.5,-0.5,-0.5);
line_:array[0..11] of integer = (0,1, 0,2, 0,3, 1,2, 1,3, 2,3);
18192123

procedure draw(color:byte);
begin
 for p:=0 to 5 do begin
  sx:=round(zoom*tetr[line_[p*2]*3])+260;
  sy:=round(zoom*tetr[line_[p*2]*3+1])+300;
    sx1:=round(zoom*tetr[line_[p*2+1]*3])+260;
  sy1:=round(zoom*tetr[line_[p*2+1]*3+1])+300;
  setcolor(color);
  line(SX,SY,sx1,sy1);
 end;



кусок программы из 2-го поста
что значат такие записи
tetr[line_[p*2]*3])+260
tetr[line_[p*2]*3+1])+300
tetr[line_[p*2+1]*3])+260
tetr[line_[p*2+1]*3+1])+300

а именно, не пойму, для чего *2, *3, прибавляем 1 в разных местах???
Malice
Цитата(18192123 @ 23.04.2007 22:32) *

а именно, не пойму, для чего *2, *3, прибавляем 1 в разных местах???

Что ж тут не понятного ? Массив Line_ заполнен так [что соединяем, с чем, что, с чем, ..] т.е. на четных местах стоих первая точка, на нечетных - вторая. По-этому p*2-четная точка, p*2+1 следующая, т.е. с которой соединение.
Массив tetr так [x, y, z, x,y,z, и т.д.], принцип тот же..
18192123
Цитата(Malice @ 23.04.2007 23:34) *

Что ж тут не понятного ? Массив Line_ заполнен так [что соединяем, с чем, что, с чем, ..] т.е. на четных местах стоих первая точка, на нечетных - вторая. По-этому p*2-четная точка, p*2+1 следующая, т.е. с которой соединение.
Массив tetr так [x, y, z, x,y,z, и т.д.], принцип тот же..

спасибо за объяснение! а для чего умножение на 3?
Malice
Цитата(18192123 @ 23.04.2007 23:56) *

спасибо за объяснение! а для чего умножение на 3?

Ну так p*3=х, p*3+1=y, p*3+2=z как раз..
Сергей
Цитата(18192123 @ 20.04.2007 8:11) *

Почему никто не хочет мне этого объяснить на моём примере?
Я хочу сделать сама, но мне нужно вначале понять, как начинать, а начинать нужно с взятия нормалей, нахождения их векторного произведения и нормирования полученного вектора....
Ну не пойму я, как выбрать в начале вектора!
( а в программе, которую оставил Malice для меня всё не понятно! Да и объёмчик внушительный!)


Сейчас я всё попробую объяснить на конкретном примере=)
Для начала я бы модифицировал сам пример для большего удобства работы.
Вообще я писал бы на объектах=), но не всё сразу.
Во первых описал бы типы:
Код

type
  TPoint3d = record {Трёхмерная точка}
    case byte of
      0: (x, y, z: real); {заданная либо тремя координатами в отдельных полях записи}
      1: (v: array[0..2] of real); {либо тремя элементами массива. Тогда 0й-x, 1й-y...}
  end;
  TNumLine = array[0..1] of integer; {тип, хранящий пару номеров вершин одного ребра}
  TNumPlane = array[0..2] of integer; {тип, хранящий тройку номеров вершин одной грани}
  TTetra = record {Тип, описывающий тетраэдр}
    Points: array[0..3] of TPoint3d;  {Все четыре вершины тетраэдра}
    Edges: array[0..5] of TNumLine;   {Все шесть ребер тетраэдра,
    заданные парами номеров соединяемых ребрами вершин из массива Points}
    Planes: array[0..3] of TNumPlane; {Все четыре треугольных грани тетраэдра.
    Каждая грань задана тройкой номеров вершин, входящих в неё. Номера вершин
    в соответствии с массивом Points.}
  end;

тогда наш тетраэдр будет задан следующей типизованной константой:
Код

const
  Tetra: TTetra = (
    Points: ( {Все точки тетраэдра}
      (X: -1; Y: -1; Z: -1), {0}
      (X: -1; Y: -1; Z:  1), {1}
      (X: -1; Y:  1; Z: -1), {2}
      (X: -1; Y:  1; Z:  1));{3}
    Edges: ( {Все рёбра тетраэдра}
      (0, 1),
      (0, 2),
      (0, 3),
      (1, 2),
      (2, 3),
      (3, 1));
    Planes: ( {Все грани тетраэдра}
      (0, 1, 2), {внимание! грани лучше задавать так, чтобы если смотреть на них}
      (0, 2, 3), {снаружи, то точки обхода грани шли против часовой стрелки}
      (0, 3, 1), {зачем это надо скажу ниже}
      (1, 2, 3))
  );

Тепрь бы я написал функции перевода координат из трёхмерных в экранные.
По-хорошему это надо делать с помощью афинных преобразований, но для начала сойдёт и выкорчевывание кода из привелённой выше по форуму программки.

Я обещал рассказать зачем надо обходить грани против часовой стрелки.
По порядку.
Грань -- это плоскость. Она задана тремя точками не лежащими на одной прямой.
К любой плоскости можно построить нормаль.
Любая точка -- это вектор. Положим все знакомы с операциями сложения/вычитания векторов, а также с операцией их векторного умножения.
Итак, плоскость задана тремя точками (векторами) a, b и c.
У нашего тетраэдра грани имеют две стороны: лицевую и изнаночную.
Положим точки a, b и c, если смотреть на грань с лицевой стороны расположены так, что двигаясь от a к b, от b к c и от c к a мы двигаемся против часовой стрелки.
Получаем два вектора: (ab)=b-a и (ac)=c-a. Векторное произведение v=(ab)*(ac) -- это вектор, перпендикулярный плоскости (abc). Прчем по правилу буравчика он будет смотреть из плоскости в нашу сторону, то есть в сторону лицевой стороны. Если нормировать этот вектор n=v/|v|, то получим как раз нормаль к плоскости abc.
Кстати, я не проверял координаты точек, которыми задан тетраэдр. Очень может быть, что они как раз не удовлетворяют требованию обхода против часовой стрелки. Я полагал, что нулевая точка тетраэдра -- это его вершина, а три другие в основании.
Так что нужно проверить, чтобы это было именно так.
Для первого раза хватит?
Потом дальше расскажу, если всё ещё интересно=).

P.S.
Я приношу свои извинения, если ошибся в синтаксисе. Писал прямо в браузере, и мог опечататься. К тому же сто лет не открывал Турбо Паскаля.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.