OpenGL в Delphi

       

Обязательно посмотрите, как работает этот красивый пример




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

const
density = 36;
Введен класс, соответствующий отдельному пятну:

type
TDrip = class public
// массивы цветов пятна
outer_color, nng_color, inner_color: Array [0.. 3] of GLfloat;
outer_radius, ring_radius: GLfloat;
procedure Draw;
procedure fill_points;
private
divisions: GLint; // деления, соответствуют плотности
points: Array [O.. density * 8 - 1] of GLfloat; // точки пятна
end;

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

const
max_drips = 20; max_ring_radius = 250.0;

Нам необходимо запоминать позицию центра каждого пятна, а также иметь массив, хранящий данные на каждое пятно:

var
dnp_position : Array [0..max_drips-l, 0. 1] of GLfloat;
first_drip, new_drip : GLint; // текущее количество пятен
drips : Array [0. .max_dnps - 1] of TDrip;

При рисовании каждого пятна рисуются треугольники, расходящиеся от центра пятна. Если тип примитива заменить на GL_LINES, в результате получатся красивые снежинки.

procedure TDrip.Draw; // метод - нарисовать отдельное пятно
var
1 : GLint;
begin
glBegin(GL__TRIANGLES) ;
For i := 0 to divisions-1 do begin
glColor4fv(@inner_color);
glVertex2f(0.0, 0.0); // треугольники, выходящие из центра пятна
glColor4fv(@ring_color);
glVertex2f(points[2*i] * ring_radius, points[2*i + 1] * ring_radius),
glVertex2f(points[2*((i+l) mod divisions)] * ring_radius,
points[(2*((i+l) mod divisions)) + 1] * ring radius);
end;
glEnd;
end;

При заполнении массива точек пятна берутся значения, равномерно лежащие внутри круга:

procedure TDrip.fill_pomts;
var
i : GLint;
theta : GLfloat;
delta : GLfloat;
begin
delta := 2.0 * PI / divisions;
theta := 0.0;
For i := 0 to divisions-1 do begin
points[2 * i] := cos(theta);
points[2 * i + 1] := sin(theta);
theta := theta + delta;
end;
end;

Если, например, синус умножить на какое-нибудь число, получатся не круги, а овалы
Пользовательская процедура create_drip вызывается при каждом щелчке кнопкой мыши, ей передаются координаты центра нового пятна и требуемый цвет:

procedure create_drip(x, у, г, g, b : GLfloat);
begin
drips [new_drip] := TDrip. Create/With drips[new_drip] do begin
divisions := density,
fill_points;
inner_color[0] := r;ring_color[0] := r;outer_color[0] := r;
inner_color [ 1 ] := g;nng_color [1] := g;outer_color [1] := g;
inner_color[2] : = b;ring_color[2] := b;outer_color[2] := b;
// альфа-компонент края пятна нулевой
inner_color[3] := 1.0;ring_color[3] := 1.0;outer_color[3] := 0.0;
ring_radius := 0.0;outer_radius := 0.0;
end;
drip_position[new_drip][0] := x;
drip_position[new_drip][1] := y;
// увеличиваем счетчик пятен
new_drip := (new_drip + 1) mod max_drips;
If (new_drip = first_drip)
then first_drip := (first_drip + 1) mod max_drips;
end;

При очередном воспроизведении кадра радиусы всех присутствующих на экране пятен увеличиваются, при достижении максимального радиуса пятно растворяется.
Анимация создается простым зацикливанием, но вряд ли этот пример будет работать где-то чересчур быстро, т к используется так называемый "альфа блэндинг" (blend - смешивать):

Procedure TfrmGL.WMPaint(var Msg: TWMPaint);
var
ps : TPaintStruct; rel_size : GLfloat; i : GLint;
begin
BeginPaint(Handle, ps);
i := first_drip;
glClear(GL_COLOR_BUFFER_BIT);
While i<>new_drip do begin
drips[i].ring_radius := drips[i].ring_radius + 1;
drips[i].outer_radius := drips[i].outer_radius + 1;
rel size := drips[i].ring_radius / max_ring_radius;
// корректируем альфа-компонент, края пятна полупрозрачные
drips[i]. nng_color [3] := 0;
drips[i].inner_color[3] := 5-5*rel_size*rel_size;
// смещаемся в центр пятна
glPushMatrix;
glTranslatef(drip_position[i][0], drip_position[i][1], 0.0);
drips[i].draw; // рисуем пятно
glPopMatrix;
// пятно достигло максимального размера
If (drips[i].ring_radius > max_ring_radius)
then first_drip := (first_drip + 1) mod max_drips;
i:=(i+l) mod max_drips;
end;
SwapBuffers(DC);
EndPaint(Handle, ps);
InvalidateRect(Handle, nil, False); // зацикливаем программу
end;

Если на вашем компьютере пример работает недостаточно быстро, можете увеличить шаг, с которым увеличиваются радиусы пятен.



Содержание раздела