ICQ - форум. Всё про ICQ.  

Вернуться   ICQ - форум. Всё про ICQ. > Мастерская > Программирование > Статьи

 
 
Опции темы Оценить тему
Старый 10.04.2009, 20:22   #1
Участник
 
Аватар для crystalbit
 
Регистрация: 28.05.2008
Сообщений: 81

ICQ: 224116

Репутация: 100
По умолчанию [delphi] Простейший 3d двиг средствами delphi. Тор.

Простейший 3d двиг средствами delphi. Тор


Итак, господа, что мы сегодня будем делать?
Мы попытаемся реализовать свой простой 3d движок и на радостях построить такой замечательный объект как тор (в простонародии бублик).

Готовый проект и немного extra примеров ищи на http://parsers.info/pub/3d

Будем работать без перспективы, чисто черчение, мы не художники. Перспективу, впрочем, потом несложно добавить - всего-то рассчитать расстояние до точки и домножить на коэффициент.


[[часть 1]]

[средства]
Мы будем использовать компонент TImage и массив Pixels в Canvas. И больше ничего, никаких компонентов ; )

[приготовления #1]
Напишем вспомогательные функции, которые будут нам давать центр изображения. Нам он не раз понадобится, и это более чем удобно.
Код:
function cx(im:TImage):integer;
begin
  result:=im.ClientWidth div 2;
end;

function cy(im:TImage):integer;
begin
  result:=im.ClientHeight div 2;
end;
[понятия]
Введем некоторые понятия для облегчения наших действий: оригинал - точка в пространстве, в котором мы будем представлять наш объект, проекция - точка на поверхности картинки. Чуть позже напишем функции для преобразования оригинала в проекцию и отображения.

[типы]
Какие типы нам понадобятся? Для начала точка в пространстве:
Код:
type
  TOriginal=record
    x:real;
    y:real;
    z:real;
  end;
Все расчеты будем проводить с типом real.
Для проекции используем уже существующий тип TPoint с целочисленными значениями x и y. При преобразовании и будет происходить округление.
Вспомним, что мы собираемся рисовать в изометрии, но их же много! Мы будем использовать прямоугольную изометрию(гуглим), но сделаем всё по уму, чтобы мы всегда могли поменять изометрию на диметрию, проекцию на проецирующую плоскость и т.д.
Вводим следующий тип:
Код:
type
  TAxonometry=record
    xkx:real;
    xky:real;
    ykx:real;
    yky:real;
    zkx:real;
    zky:real;
  end;
Что же это, собственно, такое? Это просто-напросто коэффициенты, на которые мы домножаем координаты оригинала и получаем координаты проекции. Об этом ниже.

[константы]
И константочка для изометрии:
Код:
const
  Izometry:TAxonometry=(xkx:-0.866{cos(pi/6)};
                        xky:0.5{sin(pi/6)};
                        ykx:0.866{-cos(pi/6)};
                        yky:0.5{sin(pi/6)};
                        zkx:0;
                        zky:-1;);
Будем использовать правую тройку осей с осью Z, направленной вверх.
В комментариях указаны формулы, приближенные значения которых мы видим, их достаточно легко получить. Кстати, это очень прикольный способ задания констант типа record.

[приготовления #2]
По горячим следам напишем функцию для преобразования оригинала:
Код:
function Original2Point(v:TOriginal;ax:TAxonometry;im:TImage):TPoint;
var
  p:TPoint;
begin
  with ax do
    p.X:=Round(cx(im)+xkx*v.x+ykx*v.y+zkx*v.z);
  with ax do
    p.Y:=Round(cy(im)+xky*v.x+yky*v.y+zky*v.z);

  Result:=p;
end;
Что же получается? Да, каждая координата оригинала вносит вклад в обе координаты проекции, который берем из передаваемого объекта типа TAxonometry. В нашем случае вклад координаты Z оригинала в координату X равен нулю, а в координату Y минус единице. Ибо ось Z направлена вертикально, как сказано выше, от изменения координаты по ней, проекция не сдвигается ни влево, ни вправо, а по оси Y в коспоненте TImage нумерация идёт сверху, поэтому переворачиваем. В остальных случаях оси пространства оригиналов проецируются как прямые под углом 30 градусов к горизонту, см. константу Izometry.

И рисовальня:
Код:
procedure OriginalDraw(v:TOriginal;ax:TAxonometry;c:TColor;im:TImage);
var
  p:TPoint;
begin
  p:=Original2Point(v,ax,im);
  im.Canvas.Pixels[p.X,p.Y]:=c;
end;
Тут объяснять не надо, разве что оговорюсь, что для рисования тора мы её использовать не будем, мы построим каркас из линий. Эта функция необходима для рисования ГМТ или хрен-знает-каких-тел, у которых ничего неизвестно про непрерывность.

Функции, аналогичные MoveTo и LineTo:
Код:
procedure OriginalMoveTo(v:TOriginal;ax:TAxonometry;im:TImage);
var
  p:TPoint;
begin
  p:=Original2Point(v,ax,im);
  im.Canvas.MoveTo(p.X,p.Y);
end;

procedure OriginalLineTo(v:TOriginal;ax:TAxonometry;c:TColor;im:TImage);
var
  p:TPoint;
begin
  p:=Original2Point(v,ax,im);
  im.Canvas.Pen.Color:=c;
  im.Canvas.LineTo(p.X,p.Y);
end;
С их помощью и будет сделан наш тор. Чуть позже всё рассчитаем.

[подитог]
Ну что, чувачки? Ожидали чего-то посложней? Простое домножение на коэффициент и у нас есть простейший 3d двиг. Надо сходить поесть и займемся тором. Напоминаю, что в оригинале эта статья опубликована на http://parsers.info

[[часть 2]]
[матчасть]
Строить тор будем основываясь на параметрических уравнениях:
<i>x(i,k) = (R + r cos i) cos k
y(i,k) = (R + r cos i) sin k
z(i,k) = r sin i
где пераметры i,k варьируются от 0 до 2pi</i>
Из констант нам надо объявить шаги параметров для циклов рисования горизонально и вертикально расположенных окружностей, мы их будем рисовать отдельно. Шаг для вертикальных окружностей должен быть больше, для ощущения равномерности.

[пишем процедуру]
1) Начальный этап. Объявим локальные переменные и константы.
Код:
procedure DrawTor(r1,r2:real;ax:TAxonometry;c:TColor;im:TImage);
var
  i,k:integer;
  ir,kr:real;
  curr:TOriginal;
const
  steph:real=28.8;
  stepv:real=3.6;
begin
i, k - параметры в градусах, ir, kr - переменные, подготовленные для перевода в радианы. curr - расчетная точка, steph и stepv - шаги, про которые написано чуть повыше. Число 360 должно делиться на шаги без остатка!
2) Рисуем горизонтальные окружности.
Код:
  for i:=0 to Round(360/steph) do begin
    ir:=DegToRad(i * steph);
    curr.x:=(r1 + r2 * cos(ir)) * cos(0);
    curr.y:=(r1 + r2 * cos(ir)) * sin(0);
    curr.z:=r2 * sin(ir);
    OriginalMoveTo(curr,ax,im);
    for k:=0 to 360 do begin
      kr:=DegToRad(k);
      curr.x:=(r1 + r2 * cos(ir)) * cos(kr);
      curr.y:=(r1 + r2 * cos(ir)) * sin(kr);
      curr.z:=r2 * sin(ir);
      OriginalLineTo(curr,ax,c,im);
    end; // for
  end; // for
Здесь в первом цикле перед началом вложенного рассчитывается "нулевая" точка - точка, с которой начинаем рисование окружности, т.е. ломаной линии из 360 отрезков.
3) Дорисовываем вертикальные окружности.
Код:
  for i:=0 to Round(360/stepv) do begin
    ir:=DegToRad(i * stepv);
    curr.x:=(r1 + r2 * cos(0)) * cos(ir);
    curr.y:=(r1 + r2 * cos(0)) * sin(ir);
    curr.z:=r2 * sin(0);
    OriginalMoveTo(curr,ax,im);
    for k:=0 to 360 do begin
      kr:=DegToRad(k);
      curr.x:=(r1 + r2 * cos(kr)) * cos(ir);
      curr.y:=(r1 + r2 * cos(kr)) * sin(ir);
      curr.z:=r2 * sin(kr);
      OriginalLineTo(curr,ax,c,im);
    end; // for
  end; // for
Здесь меняем местами параметры и еще кое-что, по аналогии.
4) Финал.
На форму ставим компонент TImage, свойство Align ставим alClient, чтобы занимало всё пространство.
На событие создания формы пишем:
Код:
  DrawTor(200,40,Izometry,clRed,imgOut);
>>>the end.<<<

[вывод]
В результате наших изысканий мы написали простейшие функции преобразования координат и, используя параметрические уравнения, нарисовали тор. Это всего лишь образец того, что можно сделать

(c) crystalbit, http://parsers.info, 10.04.2009
допускается копирование при сохранении копирайта
__________________
Мой скромный delphi блог
crystalbit вне форума  
 

Опции темы
Оценка этой теме
Оценка этой теме:

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход


Часовой пояс GMT +3, время: 11:50.


Перевод: zCarot
Форум Асечников © Asechka.RU

Новости Сочи