• СТАТЬЯ  Учебное пособие по OpenGL Продолжение. Начало см. выпуск 67
  • Программирование на Visual C++

    Выпуск №67б от 10 марта 2002 г. 

    СТАТЬЯ 

    Учебное пособие по OpenGL

    Продолжение. Начало см. выпуск 67

    Авторы: Фролов Антон

    Игнатенко Алексей

    Источник: Лаборатория компьютерной графики при ВМиК МГУ

    Материалы и освещение

    Для создания реалистических изображений необходимо определить как свойства самого объекта, так и свойства среды, в которой он находится. Первая группа свойств включает в себя параметры материла, из которого сделан объект, способы нанесения текстуры на его поверхность, степень прозрачности объекта. Ко второй группе можно отнести количество и свойства источников света, уровень прозрачности среды, а также модель источников света. Все эти свойства можно задавать, используя соответствующие команды OpenGL.

    Свойства материала

    Для задания параметров текущего материала используются команды

    void glMaterial[i f](GLenum face, GLenum pname, GLtype param)

    void glMaterial[i f]v(GLenum face, GLenum pname, GLtype *params)
     

    С их помощью можно определить рассеянный, диффузный и зеркальный цвета материала, а также цвет степень зеркального отражения и интенсивность излучения света, если объект должен светиться. Какой именно параметр будет определяться значением param, зависит от значения pname:

    GL_AMBIENT параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют рассеянный цвет материала (цвет материала в тени). Значение по умолчанию: (0.2, 0.2, 0.2, 1.0).
    GL_DIFFUSE параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют цвет диффузного отражения материала. Значение по умолчанию: (0.8, 0.8, 0.8, 1.0).
    GL_SPECULAR параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют цвет зеркального отражения материала. Значение по умолчанию: (0.0, 0.0, 0.0, 1.0).
    GL_SHININESS параметр params должен содержать одно целое или вещественное значение в диапазоне от 0 до 128, которое определяет степень зеркального отражения материала. Значение по умолчанию: 0.
    GL_EMISSION параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют интенсивность излучаемого света материала. Значение по умолчанию: (0.0, 0.0, 0.0, 1.0).
    GL_AMBIENT_AND_DIFFUSE эквивалентно двум вызовам команды glMaterial..() со значением pname GL_AMBIENT и GL_DIFFUSE и одинаковыми значениями params.

    Из этого следует, что вызов команды glMaterial[i f]() возможен только для установки степени зеркального отражения материала. В большинстве моделей учитывается диффузный и зеркальный отраженный свет; первый определяет естественный цвет объекта, а второй – размер и форму бликов на его поверхности.

    Параметр face определяет тип граней, для которых задается этот материал и может принимать значения GL_FRONT, GL_BACK или GL_FRONT_AND_BACK.

    Если в сцене материалы объектов различаются лишь одним параметром, рекомендуется сначала установить нужный режим, вызвав glEnable() c параметром GL_COLOR_MATERIAL, а затем использовать команду

    void glColorMaterial(GLenum face, GLenum pname)
     

    где параметр face имеет аналогичный смысл, а параметр pname может принимать все перечисленные значения. После этого, значения выбранного с помощью pname свойства материала для конкретного объекта (или вершины) устанавливается вызовом команды glColor..(), что позволяет избежать вызовов более ресурсоемкой команды glMaterial..() и повышает эффективность программы.

    Пример.

    float mamat_dif[]={0.8, 0.8, 0.8};

    float mat_amb[]= {0.2, 0.2, 0.2};

    float mat_spec[]={0.6, 0.6, 0.6};

    float shininess=0.7*128;

    /*...*/

    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_amb);

    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_dif);

    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec);

    glMaterialf(GL_FRONT, GL_SHININESS, shininess);

    Источники света

    Добавить в сцену источник света можно с помощью команд

    void glLight[i f](GLenum light, GLenum pname, GLfloat param)

    void glLight[i f](GLenum light, GLenum pname, GLfloat *params)
     

    Параметр light однозначно определяет источник, и выбирается из набора специальных символических имен вида GL_LIGHTi , где i должно лежать в диапазоне от 0 до GL_MAX_LIGHT, которое не превосходит восьми.

    Оставшиеся два параметра имеют аналогичный смысл, что и в команде glMaterial..(). Рассмотрим их назначение (вначале описываются параметры для первой команды, затем для второй): 

    GL_SPOT_EXPONENT параметр param должен содержать целое или вещественное число от 0 до 128, задающее распределение интенсивности света. Этот параметр описывает уровень сфокусированности источника света. Значение по умолчанию: 0 (рассеянный свет).
    GL_SPOT_CUTOFF параметр param должен содержать целое или вещественное число между 0 и 90 или равное 180, которое определяет максимальный угол разброса света. Значение этого параметра есть половина угла в вершине конусовидного светового потока, создаваемого источником. Значение по умолчанию: 180 (рассеянный свет).
    GL_AMBIENT параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют цвет фонового освещения. Значение по умолчанию: (0.0, 0.0, 0.0, 1.0).
    GL_DIFFUSE параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют цвет диффузного освещения. Значение по умолчанию: (1.0, 1.0, 1.0, 1.0) для LIGHT0 и (0.0, 0.0, 0.0, 1.0) для остальных.
    GL_SPECULAR параметр params должен содержать четыре целых или вещественных значения цветов RGBA, которые определяют цвет зеркального отражения. Значение по умолчанию: (1.0, 1.0, 1.0, 1.0) для LIGHT0 и (0.0, 0.0, 0.0, 1.0) для остальных.
    GL_POSITION параметр params должен содержать четыре целых или вещественных, которые определяют положение источника света. Если значение компоненты w равно 0.0, то источник считается бесконечно удаленным и при расчете освещенности учитывается только направление на точку (x, y, z), в противном случае считается, что источник расположен в точке (x, y, z, w). Значение по умолчанию: (0.0, 0.0, 1.0, 0.0).
    GL_SPOT_DIRECTION параметр params должен содержать четыре целых или вещественных числа, которые определяют направление света. Значение по умолчанию: (0.0, 0.0, –1.0, 1.0).

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

    Для использования освещения сначала надо установить соответствующий режим вызовом команды glEnable(GL_LIGHTNING), а затем включить нужный источник командой glEnable(GL_LIGHTn). 

    ПРИМЕЧАНИЕ

    Еще раз обратим внимание на то, что при выключенном освещении цвет вершины равен текущему цвету, который задается командами glColor..(). При включенном освещении цвет вершины вычисляется исходя из информации о материале, о нормалях и об источниках света.

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

    Модель освещения

    В OpenGL используется модель освещения, в соответствии с которой цвет точки определяется несколькими факторами: свойствами материала и текстуры, величиной нормали в этой точке, а также положением источника света и наблюдателя. Для корректного расчета освещенности в точке надо использовать единичные нормали, однако команды типа glScale..(), могут изменять длину нормалей. Чтобы это учитывать, используется уже упоминавшийся режим нормализации нормалей, который включается вызовом команды glEnable(GL_NORMALIZE).

    Для задания глобальных параметров освещения используются команды

    void glLightModel[i f](GLenum pname, GLenum param)

    void glLightModel[i f]v(GLenum pname, const GLtype *params)

    Аргумент pname определяет, какой параметр модели освещения будет настраиваться и может принимать следующие значения:

    GL_LIGHT_MODEL_LOCAL_VIEWER параметр param должен быть булевским и задает положение наблюдателя. Если он равен FALSE, то направление обзора считается параллельным оси –z, вне зависимости от положения в видовых координатах. Если же он равен TRUE, то наблюдатель находится в начале видовой системы координат. Это может улучшить качество освещения, но усложняет его расчет. Значение по умолчанию: FALSE.
    GL_LIGHT_MODEL_TWO_SIDE параметр param должен быть булевским и управляет режимом расчета освещенности как для лицевых, так и для обратных граней. Если он равен FALSE, то освещенность рассчитывается только для лицевых граней. Если же он равен TRUE, расчет проводится и для обратных граней. Значение по умолчанию: FALSE.
    GL_LIGHT_MODEL_AMBIENT параметр params должен содержать четыре целых или вещественных числа, которые определяют цвет фонового освещения даже в случае отсутствия определенных источников света. Значение по умолчанию: (0.2, 0.2, 0.2,1.0).
    Текстуры

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

    • выбрать изображение и преобразовать его к нужному формату

    • загрузить изображение в память

    • определить, как текстура будет наноситься на объект и как она будет с ним взаимодействовать.

    Подготовка текстуры

    Для использования текстуры необходимо сначала загрузить в память изображение нужной текстуры и передать его OpenGL.

    Считывание графических данных из файла и их преобразование можно проводить вручную. Можно также воспользоваться функцией, входящей в состав библиотеки GLAUX (для ее использования надо дополнительно подключить glaux.lib), которая сама проводит необходимые операции. Это функция

    AUX_RGBImageRec* auxDIBImageLoad(const char *file)

    где file – название файла с расширением *.bmp или *.dib. В качестве результата функция возвращает указатель на область памяти, где хранятся преобразованные данные.

    Сейчас предположим, что изображение уже загружено и рассмотрим, каким образом его можно передать механизмам OpenGL.

    При создании образа текстуры в памяти следует учитывать следующие требования.

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

    void gluScaleImage(GLenum format, GLint widthin, GL heightin, GLenum typein, const void *datain, GLint widthout, GLint heightout, GLenum typeout, void *dataout)

    В качестве значения параметра format обычно используется значение GL_RGB или GL_RGBA, определяющее формат хранения информации. Параметры widthin, heightin, widhtout, heightout определяют размеры входного и выходного изображений, а с помощью typein и typeout задается тип элементов массивов, расположенных по адресам datain и dataout. Как и обычно, то может быть тип GL_UNSIGNED_BYTE, GL_SHORT, GL_INT и так далее. Результат своей работы функция заносит в область памяти, на которую указывает параметр dataout.

    Во-вторых, надо предусмотреть случай, когда объект по размерам значительно меньше наносимой на него текстуры. Чем меньше объект, тем меньше должна быть наносимая на него текстура и поэтому вводится понятие уровней детализации текстуры. Каждый уровень детализации задает некоторое изображение, которое является, как правило, уменьшенной в два раза копией оригинала. Такой подход позволяет улучшить качество нанесения текстуры на объект. Например, для изображения размером 2×2 можно построить max(m,n)+1 уменьшенных изображений, соответствующих различным уровням детализации.

    Эти два этапа создания образа текстуры во внутренней памяти OpenGL можно провести с помощью команды

    void gluBuild2DMipmaps(GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void *data)

    где параметр target должен быть равен GL_TEXTURE_2D, components определяет количество цветовых компонент текстуры и может принимать следующие значения:

    GL_LUMINANCE только красный. (текстура будет монохромной)
    GL_LUMINANCE_ALPHA красный и alpha.
    GL_RGB красный, синий, зеленый
    GL_RGBA все компоненты.

    Параметры width, height, data определяют размеры и расположение текстуры соответственно, а format и type имеют аналогичный смысл, что и в команде gluScaleImage().

    После выполнения этой команды текстура копируется во внутреннюю память OpenGL и поэтому память, занимаемую исходным изображением текстуры можно освободить.

    В OpenGL допускается использование одномерных текстур, то есть размера 1×N, однако это всегда надо указывать, используя в качестве значения target константу GL_TEXTURE_1D. Существует одномерный аналог рассматриваемой команды – gluBuild1DMipmaps(), который отличается от двумерного отсутствием параметра height.

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

    void glGenTextures(GLsizei n, GLuint  *textures)

    надо создать n идентификаторов для используемых текстур, которые будут записаны в массив textures. Перед началом определения свойств очередной текстуры следует вызвать команду

    void glBindTexture(GLenum target, GLuint texture)

    где target может принимать значения GL_TEXTURE_1D или GL_TEXTURE_2D, а параметр texture должен быть равен идентификатору той текстуры, к которой будут относиться последующие команды. Для того, чтобы в процессе рисования сделать текущей текстуру с некоторым идентификатором, достаточно опять вызвать команду glBindTexture() c соответствующим значением target и texture. Таким образом, команда glBindTexture() включает режим создания текстуры с идентификатором texture, если такая текстура еще не создана, либо режим ее использования, то есть делает эту текстуру текущей.

    ПРИМЕЧАНИЕ

    Так как не всякая аппаратура может оперировать текстурами большого размера, целесообразно ограничить размеры текстуры до 256×256 пикселей. Использование небольших текстур повышает эффективность приложения.

    Параметры текстуры

    При наложении текстуры, как уже упоминалось, надо учитывать случай, когда размеры текстуры отличаются от размеров объекта, на который она накладывается. При этом возможно как растяжение, так и сжатие изображения, и то, как будут проводиться эти преобразования может серьезно повлиять на качество построенного изображения. Для определения положения точки на текстуре используется параметрическая система координат (s, t), причем значения s и t находятся в отрезке [0, 1]. Для изменения различных параметров текстуры применяются команды:

    void glTexParameter[i f](GLenum target, GLenum pname, GLenum param)

    void glTexParameter[i f]v(GLenum target, GLenum pname, GLenum *params)

    При этом target имеет аналогичный смысл, что и раньше, pname определяет, какое свойство будем менять, а с помощью param или params устанавливается новое значение. Возможные значения pname:

    GL_TEXTURE_MIN_FILTER параметр param определяет функцию, которая будет использоваться для сжатия текстуры. При значении GL_NEAREST будет использоваться один (ближайший), а при значении GL_LINEAR четыре ближайших элемента текстуры. Значение по умолчанию: GL_LINEAR.
    GL_TEXTURE_MAG_FILTER параметр param определяет функцию, которая будет использоваться для увеличения (растяжения) текстуры. При значении GL_NEAREST будет использоваться один (ближайший), а при значении GL_LINEAR четыре ближайших элемента текстуры. Значение по умолчанию: GL_LINEAR.
    GL_TEXTURE_WRAP_S параметр param устанавливает значение координаты s, если оно не входит в отрезок [0, 1]. При значении GL_REPEAT целая часть s отбрасывается, и в результате изображение размножается по поверхности. При значении GL_CLAMP используются краевые значения: 0 или 1, что удобно использовать, если на объект накладывается один образ. Значение по умолчанию: GL_REPEAT.
    GL_TEXTURE_WRAP_T аналогично предыдущему значению, только для координаты t.

    Использование режима GL_NEAREST значительно повышает скорость наложения текстуры, однако при этом снижается качество, так как в отличие от GL_LINEAR интерполяция не производится.

    Для того, чтобы определить, как текстура будет взаимодействовать с материалом, из которого сделан объект, используются команды

    void glTexEnv[i f](GLenum target, GLenum pname, GLtype param)

    void glTexEnv[i f]v(GLenum target, GLenum pname, GLtype *params)

    Параметр target должен быть равен GL_TEXTURE_ENV, а в качестве pname рассмотрим только одно значение GL_TEXTURE_ENV_MODE, которое наиболее часто применяется.

    Параметр param может быть равен:

    GL_MODULATE конечный цвет находится как произведение цвета точки на поверхности и цвета соответствующей ей точки на текстуре.
    GL_REPLACE в качестве конечного цвета используется цвет точки на текстуре.
    GL_BLEND конечный цвет находится как сумма цвета точки на поверхности и цвета соответствующей ей точки на текстуре с учетом их яркости.

    Общий подход к созданию текстур:

    /* нужное нам количество текстур */

    #define NUM_TEXTURES 10

    /* идентификаторы текстур */

    int TextureIDs[NUM_TEXTURES];

    /* образ текстуры */

    AUX_RGBImageRec *pImage;

    /*...*/

    /* 1) получаем идентификаторы текстур */

    glGenTextures(NUM_TEXTURES, TextureIDs);

    /* 2) выбираем текстуру для модификации параметров */

    glBindTexture(TextureIDs[i]); /* 0<=i<NUM_TEXTURES*/

    /* 3) загружаем текстуру. Размеры текстуры - степень 2 */

    pImage = dibImageLoad("texture.bmp");

    if (Texture != NULL) {

     /* 4) передаем текстуру OpenGL и задаем параметры*/

     /* выравнивание по байту */

     glPixelStorei(GL_UNPACK_ALIGNMENT,1);

     gluBuildMipmaps(GL_TEXTURE_2D, GL_RGB, pImage->sizeX, pImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, 
    pImage->data);

     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (float)GL_LINEAR);

     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (float)GL_LINEAR);

     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (float)GL_REPEAT);

     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (float)GL_REPEAT);

     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, (float)GL_REPLACE);

     /* 5) удаляем исходное изображение.*/

     free(Texture);

    } else Error();

    Координаты текстуры

    Перед нанесением текстуры на объект осталось установить соответствие между точками на поверхности объекта и на самой текстуре. Задавать это соответствие можно двумя методами: отдельно для каждой вершины или сразу для всех вершин, задав параметры специальной функции отображения.

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

    void glTexCoord[1 2 3 4][s i f d](type coord)

    void glTexCoord[1 2 3 4][s i f d]v(type *coord)

    Чаще всего используется команды вида glTexCoord2..(type s, type t), задающие текущие координаты текстуры. Вообще, понятие текущих координат текстуры аналогично понятиям текущего цвета и текущей нормали, и является атрибутом вершины. Однако даже для куба нахождение соответствующих координат текстуры является довольно трудоемким занятием, поэтому в библиотеке GLU помимо команд, проводящих построение таких примитивов, как сфера, цилиндр и диск, предусмотрено также наложение на них текстур. Для этого достаточно вызвать команду

    void gluQuadricTexture(GLUquadricObj *quadObject, GLboolean textureCoords)

    с параметром textureCoords равным GL_TRUE, и тогда текущая текстура будет автоматически накладываться на примитив.

    Второй метод реализуется с помощью команд

    void glTexGen[i f d](GLenum coord, GLenum pname, GLtype param)

    void glTexGen[i f d]v(GLenum coord, GLenum pname, const GLtype *params)

    Параметр coord определяет для какой координаты задается формула и может принимать значение GL_S,GL_T; pname может быть равен одному из следующих значений:

    GL_TEXTURE_GEN_MODE определяет функцию для наложения текстуры.

    В этом случае аргумент param принимает значения:

    GL_OBJECT_LINEAR значение соответствующей текстурной координаты определяется расстоянием до плоскости, задаваемой с помощью значения pname GL_OBJECT_PLANE (см. ниже). Формула выглядит следующим образом: g=x*xp+y*yp+z*zp+w*wp, где g-соответствующая текстурная координата (s или p), x, y, z, w – координаты соответствующей точки. xp, yp, zp, wp – коэффициенты уравнения плоскости. В формуле используются координаты объекта.
    GL_EYE_LINEAR аналогично предыдущему значению, только в формуле используются видовые координаты. Т.е. координаты текстуры объекта в этом случае зависят от положения этого объекта.
    GL_SPHERE_MAP позволяет эмулировать отражение от поверхности объекта. Текстура как бы "оборачивается" вокруг объекта. Для данного метода используются видовые координаты и необходимо задание нормалей.
    GL_OBJECT_PLANE позволяет задать плоскость, расстояние до которой будет использоваться при генерации координат, если установлен режим GL_OBJECT_LINEAR. В этом случае параметр params является указателем на массив из четырех коэффициентов уравнения плоскости.
    GL_EYE_PLANE аналогично предыдущему значению. Позволяет задать плоскость для режима GL_EYE_LINEAR

    Для установки автоматического режима задания текстурных координат необходимо вызвать команду glEnable с параметром GL_TEXTURE_GEN_S или GL_TEXTURE_GEN_P.

    Пример:

    Рассмотрим, как можно задать зеркальную текстуру. При таком наложении текстуры изображение будет как бы отражаться от поверхности объекта, вызывая интересный оптический эффект. Для этого сначала надо создать два целочисленных массива коэффициентов s_coeffs и t_coeffs со значениями (1, 0, 0, 1) и (0, 1, 0, 1) соответственно, а затем вызвать команды:

    glEnable(GL_TEXTURE_GEN_S);

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);

    glTexGendv(GL_S, GL_EYE_PLANE, s_coeffs);

    и такие же команды для координаты t с соответствующими изменениями.

    Создание спецэффектов

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

    Туман

    Добавление тумана в сцену может повысить реализм, а иногда и скрыть некоторые артефакты, которые появляются, когда в сцене присутствуют отдаленные объекты.

    Туман в OpenGL реализуется путем изменения цвета объектов в сцене в зависимости от их глубины (расстояния от точки наблюдения).

    Для включения тумана необходимо вызвать команду glEnable(GL_FOG).

    Способ вычисления интенсивности тумана можно определить с помощью команд

    void glFog[if](enum pname, t param);

    void glFog[if]v(enum pname, t params);

    Аргумент pname может принимать следующие значения:

    GL_FOG_MODE в этом случае аргумент param определяет формулу, по которой будет вычисляться интенсивность тумана в точке.

    param может принимать значения

    GL_EXP Интенсивность вычисляется по формуле f=exp(-d*z)
    GL_EXP2 Интенсивность вычисляется по формуле f=exp(-(d*z))
    GL_LINEAR Интенсивность вычисляется по формуле f=e-z/e-s

    В этих формулах z обозначает расстояние от точки, в которой вычисляется интенсивность тумана, до точки наблюдения.

    Коэффициенты d, e, s задаются с помощью следующих значений аргумента pname

    GL_FOG_DENSITY param определяет коээфициент d
    GL_FOG_START param определяет коэффициент s
    GL_FOG_END param определяет коэффициент e

    Цвет тумана задается с помощью аргумента pname, равного

    GL_FOG_COLOR в этом случае params – указатель на массив из 4х компонент цвета.

    Пример:

    Glfloat FogColor[4]={0.5, 0.5, 0.5, 1};

    glEnable(GL_FOG);

    glFogi(GL_FOG_MODE, GL_LINEAR);

    glFogf(GL_FOG_START, 20.0);

    glFogf(GL_FOG_END, 100.0);

    glFogfv(GL_FOG_COLOR, FogColor);

    Прозрачность

    Прозрачность позволяет использовать полупрозрачные объекты в сцене, что может значительно повысить реалистичность изображения.

    В OpenGL прозрачность реализуется с помощью специального режима смешения цветов (blending). Алгоритм смешения комбинирует цвета входящих пикселей (RGBA) с цветами соответствующих пикселей, уже хранящихся в буфере кадра.

    Режим включается с помощью команды glEnable(GL_BLEND).

    Определить параметры смешения можно с помощью команды:

    void glBlendFunc(enum src, enum dst)

    Параметр src определяет, как получить коэффициент k1 исходного цвета пикселя, a dst определяет способ получения коэффициента k2 для цвета в буфере кадра. Для получения результирующего цвета используется следующая формула: res=с*k1+c*k2, где с – цвет исходного пикселя, c – цвет пикселя в буфере кадра. (res, k1, k2, с c – векторы!).

    Приведем наиболее часто используемые значения агрументов src и dst.

    GL_SRC_ALPHA k=(A,A,A,A)
    GL_SRC_ONE_MINUS_ALPHA k=(1,1,1,1)-(A,A,A,A)
    GL_DST_COLOR k=(R,G,B)
    GL_ONE_MINUS_DST_COLOR k=(1,1,1,1) - (R,G,B)
    GL_DST_ALPHA k=(A,A,A,A)
    GL_DST_ONE_MINUS_ALPHA k=(1,1,1,1)-(A,A,A,A)
    GL_SRC_COLOR k=(R,G,B)
    GL_ONE_MINUS_SRC_COLOR k=(1,1,1,1)- (R,G,B)

    Пример:

    Предположим, мы хотим реализовать вывод прозрачных объектов. Коэффициент прозрачности задается alpha-компонентой цвета. alpha, равное 1 – непрозрачный объект; равное 0 – невидимый. Для реализации служит следующий код:

    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHA, GL_SRC_ONE_MINUS_ALPHA);

    ПРИМЕЧАНИЕ

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

    Все прозрачные объекты выводятся после непрозрачных.

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

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

    Буфер накопления

    Буфер накопления (accumulation buffer) – это дополнительный внутренний буфер OpenGL. В нем можно сохранять визуализированное изображение, применяя при этом попиксельно специальные операции.

    Изображение берется из буфера, выбранного на чтение командой

    void glReadBuffer(enum buf)

    Аргумент buf определяет буфер для чтения. Значения buf, равные GL_BACK, GL_FRONT, определяют соответствующие буфера для чтения.

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

    Затем картинка переносится из буфера накопления в буфер, выбранный на запись командой

    void glDrawBuffer(enum buf)

    Значение buf аналогично значению соответствующего аргумента в команде glReadBuffer.

    Все операции с буфером накопления контролируются командой

    void glAccum(enum op, GLfloat value)

    Аргумент op задает операцию над пикселями и может принимать следующие значения:

    GL_LOAD Пиксель выбирается из буфера, выбранного на чтение, его значение умножается на value и заносится в буфер накопления.
    GL_ACCUM Аналогично предыдущему, но полученное после умножения значение складывается с уже имеющимся в буфере.
    GL_MULT Эта операция умножает значение каждого пикселя в буфере накопления на value.
    GL_ADD Аналогично предыдущему, только вместо умножения используется сложение.
    GL_RETURN Изображение переносится из буфера накопления в буфер, выбранный для записи. Перед этим значение каждого пикселя умножается на value.

    Для использования буфера накопления нет необходимости вызывать какие-либо команды glEnable. Достаточно только иметь сам буфер.

    В качестве примера использования буфера накопления рассмотрим задачу устранения лестничного эффекта (antialiasing).

    Алгоритм ее решения сразу для всей сцены таков:

    Для каждого кадра выводим сцену несколько раз, каждый раз немного смещая камеру относительно начального положения (положения камер, например, могут образовывать окружность). Все сцены сохраняем в буфере накопления с коэффициентом 1/n, где n – число сцен для каждого кадра. Чем больше таких сцен (antialiasing samples) – тем хуже производительность, но лучше качество.

    for (i=0; i<samples_count; ++i)

    /* обычно samples_count лежит в пределах от 5 до 10 */

    {

     ShiftCamera(i); /* сдвигаем камеру */

     RenderScene();

     if (i==0)

      /* на первой итерации загружаем изображение */

      glAccum(GL_LOAD, 1/(float)samples_count);

     else

      /* добавляем к уже существующему */

      glAccum(GL_ADD, 1/(float)samples_count);

    }

    /* Пишем то, что получилось, назад в исходный буфер */

    glAccum(GL_RETURN, 1.0);

    ПРИМЕЧАНИЕ

    Буфер накопления редко реализуется аппаратно. Поэтому использование устранения ступенчатости сразу для всей сцены практически несовместимо с визуализацией динамических изображений с приемлемой частотой вывода кадров (frame rate).

    Трафаретный буфер

    При выводе пикселей в буфер кадра иногда возникает необходимость выводить не все пиксели, а только некоторое их подмножество, т.е. как бы наложить трафарет на изображение. Для этого OpenGL предоставляет так называемый трафаретный буфер (stencil buffer). Кроме наложения трафарета, этот буфер предоставляет еще несколько интересных возможностей.

    Прежде чем поместить пиксель в буфер кадра, механизм визуализации OpenGL позволяет выполнить сравнение (тест) между заданным значением и значением в трафаретном буфере. Если тест проходит, пиксель визуализируется в буфере кадра.

    Механизм сравнения контролируется следующими командами:

    void glStencilFunc(enum func, int ref, uint mask)

    void glStencilOp(enum sfail, enum dpfail, enum dppass)

    Аргумент ref команды StencilFunc задает значение для сравнения. Он должен принимать значение от 0 до 2-1. s – число бит на точку в трафаретном буфере.

    С помощью аргумента func задается функция сравнения. Он может принимать следующие значения:

    GL_NEVER тест никогда не проходит, т.е всегда возвращает false
    GL_ALWAYS тест проходит всегда.
    GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATE, GL_NOTEQUAL тест проходит в случае, если ref соответственно меньше значения в трафаретном буфере, меньше либо равен, равен, больше, больше либо равен или не равен.

    Аргумент mask задает маску для значений. Т.е. в итоге для трафаретного теста получаем следующую формулу: ((ref AND mask) op (svalue AND mask ))

    Команда StencilOp предназначена для определения действий над пикселем трафаретного буфера в случае положительного или отрицательного результата теста.

    Аргумент sfail задает действие в случае отрицательного результата теста, и может принимать следующие значения:

    GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT соответственно сохраняет значение в трафаретном буфере, обнуляет его, заменяет на заданное значение (ref), увеличивает, уменьшает или побитово инвертирует.

    Аргументы dpfail определяют действия в случае отрицательного результата теста на глубину в z-буфере. dppass задает действие в случае положительного результата этого теста. Аргументы принимают те же значения, что и аргумент sfail. По умолчанию все три параметра установлены на GL_KEEP.

    Для включения трафаретного теста необходимо выполнить команду glEnable(GL_STENCIL_TEST);

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

    Пример использования трафаретного теста при создании теней описан в Приложении.


    [Текст "Приложения" к сожалению не влезает даже в расширенный выпуск и всех интересующихся отсылаю к оригиналу статьи на RSDN.] 


    Это все на сегодня. Пока!

    (Алекс Jenter jenter@rsdn.ru) (Duisburg, 2001. Публикуемые в рассылке материалы принадлежат сайту RSDN.)







    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх