Здесь нет особых приемов, работа с буфером накопления отсутствует, но эта программа послужит основой для следующих примеров
Вот в проекте из подкаталога Ех61 выводится та же самая картина, но нерезкой, размытой
Замечание
Возможно, в примере эффект мало заметен, но после того, как разберем программу, вы сможете его усилить.
Чтобы смазать изображение, в буфере накопления одна и та же сцена рисуется несколько раз, каждый раз с немного измененной точкой зрения. Например, чтобы изображение двоилось, надо нарисовать сцену два раза под двумя точками зрения Чем больше мы возьмем таких точек зрения, тем более ровным окажется размытость, но такие эффекты, конечно, затормаживают воспроизведение
Можно пожертвовать качеством изображения, но повысить скорость взять небольшое количество точек зрения, но увеличить расстояние между ними в пространстве. В примере используется модуль jitter, переложение на Delphi известного модуля jitter h Модуль содержит в виде констант набор массивов, хранящих координаты точек зрения, а точнее - смещений точки зрения, разбросанных вокруг нуля по нормальному (Гаусса) закону. Массивов всего семь, массив с именем j2 содержит данные для двух точек зрения, j66 содержит j66 таких точек. Тип элементов массивов следующий:
type
jitter_point = record
х, у : GLfloat;
end;
В нашем примере введена константа, задающая, сколько "кадров" будет смешиваться в буфере накопления:
const
ACSIZE = 8;
В примере установлена ортографическая проекция:
procedure TfrmGL.FormResize(Sender: TObject);
begin
glViewport(0, 0, ClientWidth, ClientHeight);
glMatrixMode(GL_PROJECTION);
glLoadldentity;
If ClientWidth <= ClientHeight
then glOrtho (-2.25, 2.25, -2 25*ClientHeight/ClientWidth,
2.25*ClientHeight/ClientWidth, -10.0, 10.0)
else glOrtho (-2.25*ClientWidth/ClientHeight, 2.25*ClientWidth/ClientHeight, -2.25, 2.25, -10.0, 10.0);
glMatrixMode(GLJMODELVIEW);
InvalidateRect(Handle, nil, False);
end;
Из параметров команды glOrtho видно, что расстояния между левой и правой, верхней и нижней плоскостями отсечения одинаковы и равны 4 5 Это число мы будем использовать в качестве базового при кодировании эффекта размытости, как масштаб для перевода в мировые координаты:
Procedure TfrmGL.DrawScene;
var
viewport : Array[0..3] of GLint;
jitter of GLint, 222
begin
glGetlntegerv (GL_VIEWPORT, viewport) ;
// viewport[2] = ClientWidth
// viewport[3] = ClientHeight
glClear(GL_ACCUM_BUFFER_BIT);
For Jitter := 0 to ACSIZE - 1 do begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glPushMatrix;
// Эта формула преобразовывает дробное перемещение пиксела
//в мировые координаты
glTranslatef (38[jitter].x*4.5/viewport[2],
38 [jitter] .yM.5/viewport[3] , 0.0) ;
displayObjects; // рисуем сцену
glPopMatrix;
glAccum(GL_ACCUM, 1.0/ACSIZE); // в буфер накопления
end;
glAccum (GL_RETURN, 1.0); // буфер накопления - в буфер кадра
SwapBuffers(DC); // вывод кадра на экран
end;
Обратите внимание, что коэффициент яркости при заполнении буфера накопления задается таким образом, что его итоговое значение будет равно единице, т. е результирующие цвета не будут искажены.
Замечание
В примере используется массив из восьми точек, если на вашем компьютере этот пример работает слишком медленно, можете уменьшить это число до трех, а базовое число 4 5 в формулах преобразования увеличить раза в три.
Если для вас важно качество изображения, но пауза перед воспроизведением кадра недопустима, можете отказаться от двойной буферизации, а команду SwapBuffers заменить на glFlush.
Думаю, теперь этот пример для вас понятен, и мы можем перейти к следующему, где буфер накопления используется для получения эффекта фокуса Проект из подкаталога Ех62 содержит программу, рисующую всё ту же композицию. Первоначально изображение ничем не отличается от картинки из предыдущего примера.
Для получения эффекта необходимо задать базовую точку в пространстве, находящуюся в фокусе, или расстояние от наблюдателя до этой точки Точка зрения немного переносится в пространстве, перспектива немного усиливается Чем сильнее усиливается коэффициент перспективы, тем менее узкая область пространства не теряет резкость Сцена воспроизводится в искаженной перспективе, и процесс повторяется несколько раз с разных точек зрения. Для облегчения кодирования в программе написаны две вспомогательные процедуры, основная из которых AccFrustum. Первые шесть аргументов процедуры идентичны аргументам команды glFrustum.
Аргументы pixdx и pixdy задают смещение в пикселах для нерезкости Оба устанавливаются в нуль при отсутствии эффекта размытости Параметры eyedx и eyedy задают глубину области, в пределах которой сцена не размывается, если оба они равны нулю, то на сцене не будет присутствовать область, находящаяся в фокусе. Аргумент focus задает расстояние от глаза наблюдателя до плоскости, находящейся в фокусе Этот параметр должен быть больше нуля (не равен).
Поскольку процедура использует команду переноса системы координат, до ее вызова видовые параметры должны быть заданы с учетом будущего переноса:
procedure AccFrustum(left, right, bottom, top: GLdouble;
anear, afar, pixdx, pixdy, eyedx, eyedy: GLdouble; focus: GLdouble);
var
xwsize, ywsize : GLdouble; // размеры окна
dx, dy : GLdouble;
// для хранения параметров области вывода
viewport : array[0. 3] of GLint;
begin
glGetlntegerv (GL_VTEWPORT, @viewport); // получаем размеры окна
xwsize := right - left; // дистанция в пространстве по горизонтали
ywsize := top - bottom; // дистанция в пространстве по вертикали
// приращения для искажения перспективы
dx := -(pixdx*xwsize/viewport[2] + eyedx*anear/focus);
dy := -(pixdy*ywsize/viewport[3] + eyedy*anear/focus);
glMatrixMode(GL_PROJECTION);
glLoadldentity();
// меняем перспективу на чуть искаженную
glFrustum (left + dx, right + dx, bottom + dy, top + dy, anear, afar) ;
glMatrixMode(GLJMODELVIEW); glLoadldentity();
// переносим центр сцены
glTranslatef {-eyedx, -eyedy, 0.0);
end,
В принципе, этой процедуры достаточно, но для тех, кто привык пользоваться командой gluPerspective для задания перспективы, написана процедура AccPerspective, первые четыре аргумента которой идентичны аргументам gluPerspective Смысл остальных параметров мы разобрали выше:
procedure AccPerspective(fovy, aspect, anear, afar, pixdx, pixdy,
eyedx, eyedy, focus: GLdouble);
var
fov2,left,right,bottom,top : GLdouble;
begin
// половина угла перспективы, переведенного в радианы
fov2 := "fovy*Pi) / 180.0) / 2.0;
// рассчитываем плоскости отсечения
top := anear / (cos(fov2) / sin(fov2));
bottom := -top;
right := top * aspect;
left := -right;
AccFrustum (left, right, bottom, top, anear, afar, pixdx, pixdy, eyedx, eyedy, focus);
end;
Эта процедура вызывается перед каждым воспроизведением системы объектов. Для получения эффекта фокуса можете переписать вызов, например, так:
accPerspective (50.0, viewport[2]/viewport[3],
1.0, 15.0, ;j8 [jitter] .x, j8[jitter].у, 0.33*j8[jitter] .x, 0.33*;)8 [jitter] .у, 4.0);
Последний аргумент можете варьировать для удаления плоскости фокуса от глаза наблюдателя. Следующие два примера отличаются от предыдущих только наполнением сцены. В проекте из подкаталога Ех62 пока отсутствуют какие-либо эффекты, просто нарисовано пять чайников из различного материала, каждый из которых располагается на различном расстоянии от глаза наблюдателя (Рисунок 4.39).