Я пишу GLSL шейдер для вращения манипулятора. Я нацеливаюсь на стиль Autodesk Maya, 3 оси для каждого из поворотов x y и 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);
Давайте сначала перейдем к вопросу 2:
Где моя ошибка? Как предотвратить блокировку объекта для записи в
out_color
GL просто так не работает. Растеризатор создает фрагменты, и ваш фрагментный шейдер должен определить выходные значения для все из целей рендеринга, которые вы прикрепили — или он может отбросить весь фрагмент, так что ни один из них никогда не записывается в фрейм-буфер. Если вы не выполняете явную запись в выходную переменную, ее содержимое будет неопределенным — скорее всего, вы получите некоторый контент, который находится в определенных регистрах из последних вычислений, объясняя случайный шум на вашем последнем изображении. Запись в черном цвете, конечно, приведет к первому изображению.
Вы можете сделать некоторые вещи, чтобы предотвратить эту черную границу, например:
out_color
,out_color
вывод на GL_NONE
Вероятно, есть много других стратегий, которые могут достичь того же результата, но все они не очень эффективны, что приводит нас к вопросу 1: каков альтернативный правильный способ написать такой шейдер, который делает то же самое?
Каков альтернативный правильный способ написать такой шейдер, который делает
тот же самый?
Конечно, есть разные решения. Простое решение — просто использовать два разных шейдера. В любом случае вы должны переключать одинаковое значение между вызовами отрисовки, поэтому вы не можете использовать оба режима в одном вызове отрисовки. Вы также можете просто переключить шейдер.
Другая проблема заключается в том, что внутренний переключатель. Не делай этого. Вы можете ввести различные цвета и идентификаторы объектов для линий в качестве атрибутов вершин. Наличие такого переключателя в фрагментном шейдере довольно необычно и не очень эффективно. С другой стороны, у вас по-прежнему полностью однородный поток управления, поэтому вы не увидите худшего варианта с точки зрения производительности, но я все равно считаю вашу конструкцию как минимум плохим стилем.
Вам не нужно несколько целей рендеринга для этого. MRT имеет смысл только в том случае, если вам нужны одинаковые фрагменты для всех целей рендеринга, вы просто хотите вывести больше данных, чем vec4 на фрагмент, и сделать это за один проход. Но в вашем случае вы хотите на самом деле разные геометрия, и вы уже делаете это в несколько проходов, так что вы ничего не получите. MRT просто делает вещи еще сложнее в вашем случае.
Просто используйте два шейдера, и переключите шейдеры и буфер рисования (и нужен только один буфер рисования) между ними было бы простым решением.
Есть еще одна проблема с вашим кодом: glLineWidth()
со значениями помимо 1,0 осуждается. Ваш код не будет работать на современных профилях ядра GL. Если вы хотите широкие линии, вы должны нарисовать примитивы на основе traingle.