Такие операции можно выполнять с изображениями (в терминах OpenGL - пиксельными прямоугольниками) во фрагментном шейдере в том порядке, в котором они отправляются на экран. В случае если конечное изображение действительно существует (а как мы видели, во многих случаях его нет), его можно сохранить в текстуре и затем обращаться к этой текстуре из фрагментного шейдера. Если исходное и конечное изображения хранятся в памяти графического акселератора (то есть в текстурной памяти), эти операции выполняются довольно быстро, скорость ограничивается только временем доступа к памяти и скоростью выполнения операций на графическом оборудовании. Это значительно быстрее выполнения тех же операций на основном процессоре и загрузки каждый раз по шине ввода-вывода.
16.5.1. Яркость
Самый простой пример работы с изображениями - изменение яркости. Так как конечное изображение состоит только из черных пикселов (значение (0, 0, 0)), первая половина уравнения интерполяции стремится к нулю, и уравнение сводится к простому масштабированию исходных значений. Это реализовано во фрагментном шейдере, приведенном в листинге 16.3, а результаты для нескольких значений А1 pha показаны на цветном рис. 26.
Листинг 16.3. Фрагментный шейдер для изменения яркости
uniform float Alpha: void main (void)
{
gl_FragColor = gl_Color * Alpha:
}
16.5.2. Контраст
Более интересный пример - изменение контраста (листинг 16.4). Конечное изображение является серым, каждый пиксел содержит среднее значение яркости. Это значение и А1 pha вычисляются приложением и передаются в шейдер как uniform-ne-ременные. Результаты работы шейдера контраста показаны на цветном рис. 27.
Листинг 16.4. Фрагментный шейдер для изменения контраста
uniform vec3 Avgluminance; uniform float Alpha; void main (void)
{
vec3 color = mix(AvgLuminance. gl_Color, Alpha); gl_FragColor = vec4 (color, 1.0):
}
16.5.3. Насыщенность цвета
При изменении насыщенности цвета конечное изображение содержит только информацию о яркости (то есть является полутоновой версией исходного изображения). Его можно вычислить попиксельно, получив значение яркости из каждого значения RGB. Чтобы выполнить вычисления правильно, необходимо знать, в какой цветовой системе заданы значения RGB. Если они заданы в стандарте HDTV, можно использовать коэффициенты из шейдера (листинг 16.5). Результат работы этого шейдера показан на цветном рис. 28. Как можно заметить, экстраполяция дает хорошие результаты для значений, больших 1,0.
Листинг 16.5. Фрагментный шейдер для изменения насыщенности цвета
const vec3 lumCoeff = vec3 (0.2125, 0.7154. 0.0721): uniform float Alpha:
void main (void)
{
vec3 intensity = vec3 (dot(gl_Color.rgb. lumCoeff)): vec3 color = mix(intensity, gl_Color.rgb, Alpha): gl_FragColor = vec4 (color, 1.0):
}
16.5.4. Резкость
Метод уменьшения резкости приведен в листинге 16.6. Конечное изображение можно создать размыванием исходного изображения или другим способом. Интерполяция между изображениями уменьшит высокие частоты, а экстраполяция (Alpha больше 1) увеличит. В результате будет создана маска уменьшения контрастности. Результаты выполнения этого фрагментного шейдера показаны на цветном рис. 29.