Помощь - Поиск - Пользователи - Календарь
Полная версия: Закраска по методу Гуро и Фонга
Форум «Всё о Паскале» > 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.
Я приношу свои извинения, если ошибся в синтаксисе. Писал прямо в браузере, и мог опечататься. К тому же сто лет не открывал Турбо Паскаля.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.