При щелчке кнопкой мыши по поверхности окна расходятся круги случайного цвета, напоминающие чернильные разводы. Пятна смешиваются при наложении, отчего получаются картинки завораживающей красоты.
В программе введена константа, задающая плотность разводов:
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;
Если на вашем компьютере пример работает недостаточно быстро, можете увеличить шаг, с которым увеличиваются радиусы пятен.