OpenGL/JOGL: несколько вееров треугольников в массиве вершин

Я работаю над созданием умеренно простых фигур с массивами вершин, и у меня неплохо получается, но теперь я хочу нарисовать 2 (или более) треугольных веерных объекта. Есть ли способ сделать только один вызов gl.glDrawArrays(GL.GL_TRIANGLE_FAN,... или мне нужно сделать отдельный вызов для каждого вентилятора?

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

Как правильно нарисовать несколько треугольных вееров? Вот мой текущий метод рисования:

public void draw(GL gl){
if(vertices.length == 0)
    return;

gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL.GL_COLOR_ARRAY);
    gl.glEnableClientState(GL.GL_NORMAL_ARRAY);

    gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertBuff);
    gl.glColorPointer(3, GL.GL_FLOAT, 0, colorBuff);
    gl.glNormalPointer(GL.GL_FLOAT,0, normalBuff);

    // drawArrays count is num of points, not indices.
    gl.glDrawArrays(GL.GL_TRIANGLES, 0, triangleCount);
    gl.glDrawArrays(GL.GL_QUADS, triangleCount, quadCount);
    gl.glDrawArrays(GL.GL_TRIANGLE_FAN, triangleCount+quadCount, fanCount);

    gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL.GL_COLOR_ARRAY);
    gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
}

Редактировать

Я обновил соответствующий раздел рисования следующим образом:

    for(int i = 0; i < fanLength.length; i++){
        gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 
            triangleCount+quadCount+fanDist[i], fanLength[i]);
    }

Где fanDist — смещение (от начала вентиляторов) начала этого вентилятора, а fanLength — длина этого вентилятора.

Кажется, это работает, и это хорошо, но все же, это правильный способ сделать это? Есть ли способ лучше?


person dimo414    schedule 08.12.2010    source источник
comment
Пожалуйста, никогда не забывайте указывать вашу целевую версию OpenGL. :)   -  person Kos    schedule 08.12.2010
comment
Достаточно честно - я считаю, что мы используем 2.1, если я правильно помню, но мы хотели бы, чтобы это было как можно более независимым от платформы (и, следовательно, независимым от версии OpenGL?).   -  person dimo414    schedule 08.12.2010
comment
Это больше зависит от того, на какие графические процессоры вы ориентируетесь. Описанные здесь функции должны быть доступны в GL 2.1, IIRC, так что все готово :).   -  person Kos    schedule 08.12.2010


Ответы (1)


Примитивный перезапуск работает с массивами вершин и буферами вершин - без этого он не был бы действительно полезен.

Однако это правда, что это бесполезно в сочетании с glDrawArrays.

Рассмотрим две техники:

Примитивный перезапуск


Позвольте представить вам glDrawElements: Звонок вроде

glDrawArrays(mode, 0, 5);

аналогичен

GLuint idxs[] = {0, 1, 2, 3, 4}; // C code, but idea's the same in Java
glDrawElements(mode, 5, GL_UNSIGNED_INT, idxs);

Таким образом, вместо указания диапазона элементов для извлечения из массива вы указываете точные индексы этих элементов.

И тогда вы можете ввести примитивный перезапуск, используя такой массив:

GLuint PRIMITIVE_RESTART = 12345; // magic value

glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(PRIMITIVE_RESTART);
GLuint idxs[] = {0, 1, 2, 3, PRIMITIVE_RESTART, 4, 5, 6, 7};
glDrawElements(mode, 9, GL_UNSIGNED_INT, idxs);

Это нарисует веер из первых 4 вершин, затем встретит «знак» для перезапуска примитива, а затем нарисует еще один веер из последних 4 вершин.

Индексы, передаваемые DrawElements, не обязательно должны быть последовательным диапазоном! Они могут быть в любом порядке и могут повторяться по вашему желанию — это лучшая часть этой функции. На самом деле лучше повторно использовать один индекс как можно чаще, так как он будет обработан вершинным шейдером только один раз, если результат кэшируется. Таким образом, вы можете создавать такие буферы, как:

GLuint idxs[] = {0, 6, 3, 4, 6, 2, PRIMITIVE_RESTART, 2, 6, 3, 3, 5, 2}; // etc.

MultiDrawElements


В вашем случае вы можете вместо этого использовать glMultiDrawElements.

Если у вас есть, скажем, 20 вершин и вы хотите нарисовать один веер из 8 вершин, начиная с первой, и один веер из 10 вершин, начиная с 10-й, вы можете сделать что-то вроде этого:

// assuming glVertexPointer is already set
GLuint startingElements[] = {0, 9};
GLuint counts[] = {8, 10};
glMultiDrawArrays(GL_TRIANGLE_FAN, startingElements, counts, 2); // 2 fans

Таким образом, у вас немного меньше работы.


Выберите метод, который вы считаете более полезным. Я оставлю это вам, чтобы переписать это в Java/JOGL, принцип тот же, но вам придется использовать класс Buffer для всех них, я думаю.

person Kos    schedule 08.12.2010
comment
Большое спасибо, multiDrawElements кажется немного лучше. PRIMITIVE_RESTART или GL_PRIMITIVE_RESTART не подходят для JOGL. Существует GL_PRIMITIVE_RESTART_NV, но я не решаюсь просто использовать другое значение, как если бы оно было таким же. opengl.org/registry/specs/NV/primitive_restart.txt что дает мне Google, когда я ищу этот параметр, и похоже, что это та же концепция, но очень разные методы и структура. - person dimo414; 09.12.2010
comment
PRIMITIVE_RESTART — это просто имя переменной, которое я представил в своем примере кода. :) Только glPrimitiveRestartIndex связан с этим API GL. - person Kos; 09.12.2010
comment
Ах, моя ошибка - я думаю, я предполагал, что это нужно сначала включить, и поэтому будет константа GL_, описывающая это. - person dimo414; 15.12.2010
comment
Если вы решите использовать метод примитивного перезапуска, не забудьте: - glEnable(GL_PRIMITIVE_RESTART); я не знаю JOGL, поэтому я не знаю, нужно ли вам делать это в нем, но если есть программисты на C, которые читают это, он может их сохранить МНОГО времени (без этой строки это может привести к segfaults). - person aastefian; 06.11.2018