OpenGL выборочный закадровый рендеринг на одну из 2 текстур в одном фрагментном шейдере
Я пишу GLSL шейдер для вращения манипулятора. Я нацеливаюсь на стиль Autodesk Maya, 3 оси для каждого из поворотов xy и z и одна ось над ними для вращения вдоль передней оси камеры равна 4;
Пользователь может ограничить результат своих манипуляций определенной осью, выбрав его непосредственно в области просмотра.
Чтобы включить выбор объектов, я использую кадровый буфер custrom с 2 прикрепленными текстурами в качестве целей рендеринга - один для цвета, один для идентификаторов выделения.
Проблема состоит в том, что наличие четырех осей, нарисованных в режиме GL_LINES, создает проблему для пользователя. Если линии нарисованы тонкими и красивыми, пользователь должен очень точно щелкнуть по ним, чтобы активировать конкретную ось, потому что он должен точно выбрать пиксель с соответствующим object_id оси. Я хочу облегчить свою жизнь, создав 2 отдельных рисунка. Тонкие линии для цветного вывода и в 3 раза более широкие линии для объективного вывода текстуры.
Я новичок в openGL. Теперь моя наивная логика передает булеву форму, которая указывает фрагменту шейдера, нужно ли рисовать в цветном или объективном буфере.
Вот команды рисования с переданным селектором render_target
GLfloat lw;
glGetFloatv(GL_LINE_WIDTH,&lw);
glUniform1ui(7,0);//render to RGB
glDrawArrays(GL_POINTS,0,1);
glUniform1ui(7,1);//render to OBJECTID
glLineWidth(10);
glDrawArrays(GL_POINTS,0,1);
glLineWidth(lw);
А вот фрагмент шейдера:
#version 450
//inputs
layout(location=6) uniform uint manipulator_selected_axis;
layout(location=7) uniform uint render_to_selection_buffer;
//outputs
layout(location=0) out vec4 out_color;
layout(location=1) out float out_objectid;
void main(void)
{
if (render_to_selection_buffer==1)
{
switch (gl_PrimitiveID)
{
case 0: //CAMERA FORWARD
out_objectid=float(253)/256;
break;
case 1: //X
out_objectid=float(250)/256;
break;
case 2: //Y
out_objectid=float(251)/256;
break;
case 3: //Z
out_objectid=float(252)/256;
break;
}
out_color=vec4(0,0,0,0);
}
else
{
vec4 active_axis_color=vec4(1.0,1.0,0.0,1.0);
switch(gl_PrimitiveID)
{
case 0: //CAMERA FORWARD
if(manipulator_selected_axis==0)
out_color=active_axis_color;
else
out_color =vec4(1.0,0.5,1.0,1.0);
break;
case 1: //X
if(manipulator_selected_axis==1)
out_color=active_axis_color;
else
out_color =vec4(1.0,0.0,0.0,1.0);
break;
case 2: //Y
if(manipulator_selected_axis==2)
out_color=active_axis_color;
else
out_color =vec4(0.0,1.0,0.0,1.0);
break;
case 3: //Z
if(manipulator_selected_axis==3)
out_color=active_axis_color;
else
out_color =vec4(0.0,0.0,1.0,1.0);
break;
case 4:
out_color =vec4(0.6,0.6,0.6,1.0);
break;
}
out_objectid=0;
}
}
Мне действительно не нравится логика, которую я использую для этого, потому что она выглядит некрасивой и неоптимизированной. Во-первых, потому что шейдер должен запускаться дважды, и у него есть довольно сложный геометрический шейдер, где я делаю генерацию кругов. Во-вторых, потому что в моем фрагментном шейдере много чего-то еще, и мне сказали, что gpu не любит ветвления.
Итак, ВОПРОС 1, каков альтернативный правильный способ написать такой шейдер, который делает то же самое? ВОПРОС 2 более технический.
Здесь я предоставляю скриншот того, что я получаю при вызове 2 glDrawArrays. Как вы можете видеть, у меня есть черные контуры в моем цветном буфере. Проблема состоит в том, что когда шейдер framgent выполняет код рендеринга для цели out_objectid, он все еще записывает некоторую грязную случайную информацию в out_color, как показано на рисунке ниже, поэтому мне пришлось перезаписать его с помощью out_color=vec4(0,0,0,0). По крайней мере, это дает чистый черный цвет.
Итак, ВОПРОС 2. Где моя ошибка? Как я могу предотвратить блок objectid для записи в out_color? Спасибо ОБНОВЛЕНИЕ: После того, что мне предложили, я пришел к такому эффекту. Я не могу описать, что происходит внутри openGL. Похоже, что он все еще пишет в GL_DEPTH_COMPONENT и делает несколько неопределенных тестов глубины, которые приводят к такому результату. Я не хочу никаких записей глубины во время этого вызова отрисовки, потому что манипулятор всегда находится сверху. Может быть, вы могли бы дать мне дополнительный совет.
Чертежный код
glBindBuffer(GL_ARRAY_BUFFER,0);
glUseProgram(program_id_ctl_manip_color);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glUniform4f(0,0,0,0,1);
glUniform1f(5,0.67);
glUniform1ui(6,manipulator_axis_highlight);
glUniformMatrix4fv(10,1,GL_TRUE,cam.matrix_view.m);
glUniformMatrix4fv(14,1,GL_TRUE,cam.matrix_projection_ortho.m);
glUniformMatrix4fv(18,1,GL_TRUE,cam.matrix_camera.m);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glDrawArrays(GL_POINTS,0,1);
GLfloat lw;
glGetFloatv(GL_LINE_WIDTH,&lw);
glLineWidth(6);
glUseProgram(program_id_ctl_manip_objectid);
glUniform4f(0,0,0,0,1);
glUniform1f(5,0.67);
glUniform1ui(6,manipulator_axis_highlight);
glUniformMatrix4fv(10,1,GL_TRUE,cam.matrix_view.m);
glUniformMatrix4fv(14,1,GL_TRUE,cam.matrix_projection_ortho.m);
glUniformMatrix4fv(18,1,GL_TRUE,cam.matrix_camera.m);
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glDrawArrays(GL_POINTS,0,1);
GLenum buffers[2]={GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1};
glDrawBuffers(2,buffers);
glLineWidth(lw);
1 ответ
Давайте сначала перейдем к вопросу 2:
Где моя ошибка? Как я могу предотвратить блок objectid для записи в out_color
GL просто так не работает. Растеризатор создает фрагменты, и ваш фрагментный шейдер должен определить выходные значения для всех целей рендеринга, которые вы прикрепили - или он может отбросить весь фрагмент, чтобы ни один из его выходов никогда не записывался в кадровый буфер. Если вы не выполняете явную запись в выходную переменную, ее содержимое будет неопределенным - скорее всего, вы получите некоторый контент, который находится в определенных регистрах из последних вычислений, объясняя случайный шум на вашем последнем изображении. Запись в черном цвете, конечно, приведет к первому изображению.
Вы можете сделать некоторые вещи, чтобы предотвратить эту черную границу, например:
- включить альфа-смешивание и записать полностью прозрачные пиксели на ваш
out_color
, - просто установите буфер рисования для вашего
out_color
вывод наGL_NONE
Вероятно, существует множество других стратегий, которые могут достичь того же результата, но все они не очень эффективны, что приводит нас к вопросу 1: каков альтернативный правильный способ написания такого шейдера, который делает то же самое?
Какой альтернативный способ написать такой шейдер, который делает то же самое?
Конечно, есть разные решения. Простое решение - просто использовать два разных шейдера. В любом случае вы должны переключать одинаковое значение между вызовами отрисовки, поэтому вы не можете использовать оба режима в одном вызове отрисовки. Вы также можете просто переключить шейдер.
Другая проблема заключается в том, что внутренний переключатель. Не делай этого. Вы можете ввести различные цвета и идентификаторы объектов для линий в качестве атрибутов вершин. Наличие такого переключателя в фрагментном шейдере довольно необычно и не очень эффективно. С другой стороны, у вас по-прежнему полностью однородный поток управления, поэтому вы не увидите худшего варианта с точки зрения производительности, но я все равно считаю вашу конструкцию как минимум плохим стилем.
Вам не нужно несколько целей рендеринга для этого. MRT имеет смысл, только если вам нужны одинаковые фрагменты для всех целей рендеринга, вы просто хотите вывести больше данных, чем vec4 на фрагмент, и сделать это за один проход. Но в вашем случае вы действительно хотите другую геометрию, и вы делаете это уже в несколько проходов, так что вы ничего не получите. MRT просто делает вещи еще сложнее в вашем случае.
Просто используйте два шейдера, и переключите шейдеры и буфер рисования (и нужен только один буфер рисования) между ними было бы простым решением.
Есть еще одна проблема с вашим кодом: glLineWidth()
со значениями кроме 1.0 не рекомендуется. Ваш код не будет работать на современных профилях ядра GL. Если вы хотите широкие линии, вы должны нарисовать примитивы на основе traingle.