Как мне получить текущий цвет фрагмента? - PullRequest
29 голосов
/ 30 апреля 2010

Я пытаюсь обернуть голову вокруг шейдеров в GLSL, и я нашел некоторые полезные ресурсы и учебные пособия, но я продолжаю сталкиваться с проблемой чего-то, что должно быть фундаментальным и тривиальным: как мой фрагментный шейдер получает цвет текущего фрагмента?

Вы устанавливаете окончательный цвет, говоря gl_FragColor = whatever, но, очевидно, это значение только для вывода. Как вы получаете исходный цвет ввода, чтобы вы могли выполнять вычисления на нем? Это должно быть где-то в переменной, но если кто-то там знает его имя, он, похоже, не записал его ни в каком учебнике или документации, с которыми я столкнулся до сих пор, и это заставляет меня взбираться на стену.

Ответы [ 7 ]

14 голосов
/ 30 апреля 2010

Фрагментный шейдер получает gl_Color и gl_SecondaryColor в качестве атрибутов вершины. Он также получает четыре переменных: gl_FrontColor, gl_FrontSecondaryColor, gl_BackColor и gl_BackSecondaryColor, в которые он может записывать значения. Если вы хотите передать исходные цвета прямо, вы должны сделать что-то вроде:

gl_FrontColor = gl_Color;
gl_FrontSecondaryColor = gl_SecondaryColor;
gl_BackColor = gl_Color;
gl_BackSecondaryColor = gl_SecondaryColor;

Фиксированная функциональность в конвейере, следующем за вершинным шейдером, затем закрепит их в диапазоне [0..1] и выяснит, является ли вершина лицевой или обратной. Затем он будет интерполировать выбранный (передний или задний) цвет, как обычно. Затем фрагментный шейдер получит выбранные фиксированные интерполированные цвета как gl_Color и gl_SecondaryColor.

Например, если вы нарисовали стандартный «треугольник смерти», например:

glBegin(GL_TRIANGLES);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(-1.0f, 0.0f, -1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(1.0f, 0.0f, -1.0f);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3d(0.0, -1.0, -1.0);
glEnd();

Тогда вершинный шейдер выглядит так:

void main(void) {
    gl_Position = ftransform();
    gl_FrontColor = gl_Color;
}

с фрагментным шейдером, подобным этому:

void main() {
    gl_FragColor = gl_Color;
}

будет передавать цвета, как если бы вы использовали конвейер с фиксированной функциональностью.

12 голосов
/ 30 апреля 2010

Если вы хотите сделать многопроходный рендеринг, т.е. если вы рендерили в кадровый буфер и хотите перейти на второй проход рендеринга, когда вы используете предыдущий рендеринг, то ответом будет:

  1. Визуализация первого прохода к текстуре
  2. Свяжите эту текстуру для второго прохода
  3. Доступ к конфиденциально отображаемому пикселю в шейдере

Код шейдера для 3.2:

uniform sampler2D mytex; // texture with the previous render pass

layout(pixel_center_integer) in vec4 gl_FragCoord;
// will give the screen position of the current fragment

void main()
{
  // convert fragment position to integers
  ivec2 screenpos = ivec2(gl_FragCoord.xy);
  // look up result from previous render pass in the texture
  vec4 color = texelFetch(mytex, screenpos, 0);
  // now use the value from the previous render pass ...
}

Другим способом обработки визуализированного изображения будет OpenCL с OpenGL -> OpenCL interop. Это позволяет больше вычислений, как ЦП.

6 голосов
/ 30 апреля 2010

Если то, что вы называете "текущим значением фрагмента", является значением цвета пикселя, которое было в целевом объекте рендеринга до запуска фрагментирующего шейдера, то нет, оно недоступно.

Основная причина этого заключается в том, что потенциально, во время работы вашего фрагментного шейдера, он еще не известен. Фрагментные шейдеры работают параллельно, потенциально (в зависимости от того, какое оборудование) влияют на один и тот же пиксель, и отдельный блок, считывающий данные из некоторого вида FIFO, обычно отвечает за их последующее слияние. Это объединение называется «Смешивание» и еще не является частью программируемого конвейера. Это фиксированная функция, но у нее есть несколько различных способов объединить то, что сгенерировал ваш фрагментный шейдер, с предыдущим значением цвета пикселя.

4 голосов
/ 01 сентября 2010

Вам нужно сэмплировать текстуру с текущими координатами в пикселях, что-то вроде этого

vec4 pixel_color = texture2D(tex, gl_TexCoord[0].xy);

Обратите внимание, - как я видел, texture2D устарела в спецификации GLSL 4.00 - просто ищите похожие текстуры ... функции выборки.

Также иногда лучше указать собственные пиксельные координаты вместо gl_TexCoord [0] .xy - в этом случае напишите вершинный шейдер, например:

varying vec2 texCoord;

void main(void)
{
   gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0 );
   texCoord = 0.5 * gl_Position.xy + vec2(0.5);     
}

А во фрагментном шейдере используйте эту переменную texCoord вместо gl_TexCoord [0] .xy.

Удачи.

3 голосов
/ 30 апреля 2010

Весь смысл вашего фрагментного шейдера - решить, какой будет выходной цвет. Как вы это сделаете, зависит от того, что вы пытаетесь сделать.

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

1 голос
/ 24 сентября 2013

Конвейер графического процессора имеет доступ к базовой информации о пикселях сразу после запуска шейдеров. Если ваш материал прозрачный, на стадии смешивания конвейера будут объединены все фрагменты.

Обычно объекты смешиваются в порядке их добавления к сцене, если только они не были упорядочены алгоритмом z-буферизации. Сначала следует добавить непрозрачные объекты, а затем аккуратно добавить прозрачные объекты в порядке смешивания.

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

Установка функций смешивания SRC и DST для прозрачных объектов дает вам доступ к предыдущему смешиванию различными способами.

Здесь вы можете использовать альфа-свойство вашего выходного цвета, чтобы сделать действительно причудливое смешивание. Это наиболее эффективный способ доступа к выходным данным кадрового буфера (пикселям), поскольку он работает за один проход (рис. 1) конвейера графического процессора.

enter image description here
Рис. 1 - один проход

Если вам действительно нужно multi pass (рис. 2), тогда вы должны нацелить выходные данные кадрового буфера на дополнительный текстурный блок, а не на экран, и скопировать эту целевую текстуру на следующий проход, и так нацеливание на экран в последнем проходе. Каждый проход требует как минимум двух переключений контекста.

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

enter image description here
* 1 035 * Рис. 2 - Multi Pass

Я прибег к словесному описанию с конвейерными диаграммами, чтобы избежать устаревания, поскольку язык шейдеров (Slang / GLSL) может быть изменен.

0 голосов
/ 09 ноября 2012

how-do-i-get-i-get-the-color-of-a-фрагмента

Некоторые говорят, что это невозможно, но я говорю, что это работает для меня:

//Toggle blending in one sense, while always disabling it in the other.   
void enableColorPassing(BOOL enable) {
 //This will toggle blending - and what gl_FragColor is set to upon shader execution
   enable ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
   //Tells gl - "When blending, change nothing" 
   glBlendFunc(GL_ONE, GL_ZERO); 
}

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

Ну, по крайней мере, у меня это работает.

...