Вернуться наверх
aco.ifmo.ru photonic
вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


Визуализация растровых примитивов


OpenGL позволяет визуализировать растровые графические примитивы двух типов: bitmap (массив битов) и image (растровая картинка). Также, OpenGL предоставляет особый тип растровых объектов - текстуры. Битовые массивы и растровые картинки отображаются в плоскости порта вывода. Текстуры же особым образом формируются и натягиваются на многоугольники.

Битовые массивы

Bitmap представляет собой прямоугольный массив значений 0 и 1. Те пикселы которе имеют значение 1 будут отображаться текущим цветом, а пикселы со значением 0 никак не повлияют на существующее изображение. Bitmap используются для отображения текстовых символов и всевозможных меток на графиках, точечных диаграммах и т.п.

Для отображения массива битов используются две функции. Функция glRasterPos*() используется для установки положения нижнего левого угла отображаемого растра, а glBitmap() - непосредственно для отрисовки одного битового массива.

void glBitmap(GLsizei width, GLsizei height, GLfloat xbo, GLfloat ybo, GLfloat xbi, GLfloat ybi, const GLubyte *bitmap);

Начало координат помещается в текущее положение растра. Если начало находится за пределами порта вывода, то битовый массив не отображается. Первые два аргумента определяют ширину и высоту в пикселах. Ширина может быть любой, но при хранении она должна быть дополнена до величины кратной 8. Аргументы xbo и ybo определяют начало координат растрового массива, которое будет совмещено с текущим положением растра. Аргументв xbi и ybi определяют величину смещения текущего положения растра после отображения битового массива. Это очень удобно при отображении текстовых символов, когда начало вывода следующего символа можно расположить сразу после предыдущего. Последний аргумент представляет собой ссылку на битовый массив.

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

При работе с битовыми массивами требуется настройка следующих параметров:

  1. Порядок следования байт (GL_UNPACK_SWAP_BYTES) по умолчанию FALSE. Это значит, что байты расположены в правильном порядке и распаковка не требуется.
  2. Режим выравнивания (GL_UNPACK_ALIGNMENT) может принимать значения 1, 2, 4, 8, а по умолчанию имеет значение 4. На многих платформах операции с массивами выполняются гораздо эффективнее если они выровнены в памяти определённым образом. Например, на 32-bit системах наиболее эффективно работать с данными, размер которых кратен 32-bit. Поэтому при работе с данными часто стараются выравнивать данные до 32-bit, дополняя их 0.

// Метка в виде крестика
GLubyte bitmap_mark[]=
{
    0x10, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x10
};

// Свойства распаковки
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

// Цвет метки
glColor3d(1.,0.,0.);

// Позиция вывода растра
glRasterPos2d(25.,25.);

// Вывод битового массива
glBitmap(7,7,4,3,0,0,bitmap_mark);

Растровые картинки

Растровая картинка, в отличии от битовой, содержит в себе информацию о цвете каждого пиксела. Работа с картинками очень похожа на работу с битовыми массивами. Растровая картинка может быть сформирована в программе путём вычислений, получена из файла в графическом формате (OpenGL таких функций не предоставляет), скопирована из любой области экрана средствами OpenGL.

OpenGL предостаяляет три основные функции для работы с растровми изображениями:

  • glDrawPixels - записывет прямоугольную область пикселов из области памяти в буфер кадра в то место, которое ранее определено функцией glRasterPos;
  • glCopyPixels - копирует прямоугольную область пикселов из одной части буфера кадра в другой;
  • glReadPixels - считывает прямоугольную область пикселов из буфера кадра и помещает ее в массив в памяти.

Функция для отображения растрового изображения имеет следующий прототип:

void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);

width и height определяют размеры отображаемого растрового массива. Левый нижний угол массива будет расположен в текущей позиции растра.

Значение переменной format Формат пикселов
GL_RGB (R, G, B)
GL_RGBA (R, G, B, A)
GL_RED Только красная компонента
GL_GREEN Только зелёная компонента
GL_BLUE Только синяя компонента
GL_LUMINANCE Яркость
Значение переменной type Тип данных
GL_UNSIGNED_BYTE беззнаковое 8-bit целое (unsigned char или GLubyte)
GL_BYTE знаковое 8-bit целое (char или GLbyte)
GL_BITMAP беззнаковое 8-bit целое (unsigned char или GLubyte) в том же формате, что и битовый массив
GL_UNSIGNED_SHORT беззнаковый 16-bit целый (unsigned short или GLushort)
GL_SHORT знаковый 16-bit целый (short или GLshort)
GL_UNSIGNED_INT беззнаковый 32-bit целый (unsigned int или GLuint)
GL_INT знаковый 32-bit целый (int или GLint)
GL_FLOAT с плавающей точкой (float или GLfloat)

Последний аргумент в этой функции является указателем на нулевой элемент отображаемого массива.

Аналогичный прототип имеет функция для сохранения прямоугольной области экрана в памяти:

void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);

Функция для копирования блока пикселов имеет следующий прототип:

void glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum buffer);

Положение и размеры копируемой области определяют первые четыре параметра. Новое положение левого нижнего угла копируемой области определяется текущей позицией растра. Переменная buffer указывает, какой буфер копируется (нас интересует GL_COLOR). glCopyPixels() выполняет glReadPixels() за которым сразу же следует glDrawPixels(), но без сохранения данных в промежуточном массиве.

При работе с растровыми картинками необходимо помнить о том, что если текущее положение растра будет находиться за пределами порта вывода, то, если не предпринять специальных мер, то растровая картинка отображается не будет. Но всегда можно только часть растровой картинки. Необходимо снова воспользоваться функцией glPixelStore, которая позволит правильно указать библиотеке OpenGL, какая часть растровой картинки должна быть отображена в области порта вывода. Константы GL_UNPACK_SKIP_ROWS, GL_UNPACK_SKIP_PIXELS и GL_UNPACK_ROW_LENGTH определяют количество строк и пикселов, которые следует пропустить и длину строки соответственно.

glPixelStorei(GL_UNPACK_ROW_LENGTH, 100); //длина строки
glPixelStorei(GL_UNPACK_SKIP_ROWS, 10); // сколько строк пропустить?
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 10); // сколько пикселов пропустить в каждой строке?

В процессе преобразования растровой картинки из массива в буфер кадра над данными могут выполняться и другие операции. Например, после проведения вычислений диапазон значений яркости пикселов находится в диапазоне от -10.0 до 10.0, а не от 0.0 до 1.0, как того требует функция glDrawPixels. Поэтому, перед вызовом функции glDrawPixels необходимо преобразовать диапазон значений яркости с помощью функции glPixelTransfer() следующим образом:

glPixelTransferf(GL_RED_BIAS, -10.f);
glPixelTransferf(GL_GREEN_BIAS, -10.f );
glPixelTransferf(GL_BLUE_BIAS, -10.f );
glPixelTransferf(GL_RED_SCALE, 0.05);
glPixelTransferf(GL_GREEN_SCALE, 0.05);
glPixelTransferf(GL_BLUE_SCALE, 0.05);

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

void glPixelZoom(GLfloat zoomx, GLfloat zoomy);

По умолчанию значения zoomx и zoomy равны 1 и это значит, что размер одного пиксела растрового изображения равен 1 пикселу на экране. Если zoomx и zoomy больше 1, то размер одного пиксела изображения будет больше пиксела экрана в заданное число раз. Если zoomx и zoomy ментше 1, то размер одного пиксела изображения будет меньше пиксела экрана в заданное число раз. zoomx и zoomy можно задавать отрицательными. Это будет приводить к перевороту растрового изображения.

Пример:

#include <GL/glut.h>

const int checkImageWidth=256;
const int checkImageHeight=256;
GLubyte checkImage[checkImageHeight][checkImageWidth];

static int x_pos, y_pos, x_0, y_0;
static bool flag;

void Init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);

// Формирование растра с градиентной заливкой
for (int i = 0; i < checkImageHeight; i++)
{
for (int j = 0; j < checkImageWidth; j++)
{
checkImage[i][j] = (i+j)/2.;
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

x_pos=-checkImageWidth/2;
y_pos=-checkImageHeight/2;

flag=false;
}

void Reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-w/2., w/2., -h/2., h/2.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void Motion(int x, int y)
{
if(flag)
{
x_pos+=(x-x_0);
y_pos+=(y_0-y);
x_0=x;
y_0=y;
glutPostRedisplay();
}
}

void Mouse(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON)
{
if(state == GLUT_DOWN)
{
flag=true;
x_0=x;
y_0=y;
}
else
{
flag=false;
}
}
}

void Draw(void)
{
glClear(GL_COLOR_BUFFER_BIT);

//Запомнить атрибут
glPushAttrib(GL_CURRENT_BIT);

//Вычислить предварительное положение начала вывода битового массива
double x, y, z;

//Видовая матрица
GLdouble modelMatrix[16];
glGetDoublev(GL_MODELVIEW_MATRIX , modelMatrix);
//Матрица проекций
GLdouble projMatrix[16];
glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
//Координаты области вывода
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);

gluUnProject(viewport[0]+1, viewport[1]+1, 0, modelMatrix, projMatrix, viewport, &x, &y, &z);

int dx, dy;
dx=dy=0;

int xp, yp;
xp=x_pos;
yp=y_pos;

if(y_pos < y)
{
dy=y-y_pos;
yp=y;
}

if(x_pos < x)
{
dx=x-x_pos;
xp=x;
}

//Установить положение начала вывода битового массива
glRasterPos2f(xp, yp);
//Задать атрибуты вывода пикселов
glPixelStorei(GL_UNPACK_ROW_LENGTH, checkImageWidth); //длина строки
glPixelStorei(GL_UNPACK_SKIP_ROWS, dy); // сколько строк пропустить?
glPixelStorei(GL_UNPACK_SKIP_PIXELS, dx); // сколько пикселов пропустить в каждой строке?
//Отобразить пикселы на экране
glDrawPixels(checkImageWidth-dx, checkImageHeight-dy, GL_LUMINANCE, GL_UNSIGNED_BYTE, checkImage);

//Вернуть исходные значения атрибутам вывода
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

//Вернуть атрибут
glPopAttrib();

glFlush();

glutSwapBuffers();
}

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(512, 512);
glutInitWindowPosition(100, 100);
glutCreateWindow("Raster Test");
Init();
glutDisplayFunc(Draw);
glutReshapeFunc(Reshape);
glutMotionFunc(Motion);
glutMouseFunc(Mouse);
glutMainLoop();
return 0;
}